use grammers_crypto::DecryptionError;
use grammers_tl_types as tl;
use num_bigint::BigUint;
use std::error::Error;
use std::fmt;
#[derive(Debug, Clone, Copy)]
pub enum DeserializeError {
BadAuthKey { got: i64, expected: i64 },
BadMessageId { got: i64 },
NegativeMessageLength { got: i32 },
TooLongMessageLength { got: usize, max_length: usize },
TransportError { code: i32 },
MessageBufferTooSmall,
DecompressionFailed,
UnexpectedConstructor { id: u32 },
DecryptionError(DecryptionError),
}
impl Error for DeserializeError {}
impl fmt::Display for DeserializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::BadAuthKey { got, expected } => write!(
f,
"bad server auth key (got {}, expected {})",
got, expected
),
Self::BadMessageId { got } => write!(f, "bad server message id (got {})", got),
Self::NegativeMessageLength { got } => {
write!(f, "bad server message length (got {})", got)
}
Self::TooLongMessageLength { got, max_length } => write!(
f,
"bad server message length (got {}, when at most it should be {})",
got, max_length
),
Self::TransportError { code } => {
write!(f, "transpot-level error, http status code: {}", code.abs())
}
Self::MessageBufferTooSmall => write!(
f,
"server responded with a payload that's too small to fit a valid message"
),
Self::DecompressionFailed => write!(f, "failed to decompress server's data"),
Self::UnexpectedConstructor { id } => write!(f, "unexpected constructor: {:08x}", id),
Self::DecryptionError(ref error) => write!(f, "failed to decrypt message: {}", error),
}
}
}
impl From<tl::errors::DeserializeError> for DeserializeError {
fn from(error: tl::errors::DeserializeError) -> Self {
use tl::errors::DeserializeError::*;
match error {
UnexpectedEof => DeserializeError::MessageBufferTooSmall,
UnexpectedConstructor { id } => DeserializeError::UnexpectedConstructor { id },
}
}
}
impl From<DecryptionError> for DeserializeError {
fn from(error: DecryptionError) -> Self {
Self::DecryptionError(error)
}
}
#[derive(Debug)]
pub enum RequestError {
RPCError(RpcError),
Dropped,
BadMessage {
code: i32,
},
Deserialize(DeserializeError),
}
impl RequestError {
pub fn should_retransmit(&self) -> bool {
match self {
Self::RPCError(_) => false,
_ => true,
}
}
}
#[derive(Debug, PartialEq)]
pub struct RpcError {
pub code: i32,
pub name: String,
pub value: Option<u32>,
}
impl Error for RpcError {}
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "rpc error {}: {}", self.code, self.name)?;
if let Some(value) = self.value {
write!(f, " (value: {})", value)?;
}
Ok(())
}
}
impl From<tl::types::RpcError> for RpcError {
fn from(error: tl::types::RpcError) -> Self {
if let Some(value) = error
.error_message
.split(|c: char| !c.is_digit(10))
.find(|s| !s.is_empty())
{
let mut to_remove = String::with_capacity(1 + value.len());
to_remove.push('_');
to_remove.push_str(value);
Self {
code: error.error_code,
name: error.error_message.replace(&to_remove, ""),
value: Some(value.parse().unwrap()),
}
} else {
Self {
code: error.error_code,
name: error.error_message.clone(),
value: None,
}
}
}
}
impl From<DeserializeError> for RequestError {
fn from(error: DeserializeError) -> Self {
Self::Deserialize(error)
}
}
impl From<tl::errors::DeserializeError> for RequestError {
fn from(error: tl::errors::DeserializeError) -> Self {
RequestError::from(DeserializeError::from(error))
}
}
#[derive(Debug, PartialEq)]
pub enum TransportError {
MissingBytes(usize),
UnexpectedData(&'static str),
}
impl Error for TransportError {}
impl fmt::Display for TransportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "transport error: ")?;
match self {
TransportError::MissingBytes(n) => write!(f, "need {} bytes", n),
TransportError::UnexpectedData(what) => write!(f, "unexpected data: {}", what),
}
}
}
#[derive(Debug)]
pub enum AuthKeyGenError {
InvalidResponse {
error: tl::errors::DeserializeError,
},
InvalidNonce {
got: [u8; 16],
expected: [u8; 16],
},
InvalidPQSize {
size: usize,
},
UnknownFingerprints {
fingerprints: Vec<i64>,
},
DHParamsFail,
InvalidServerNonce {
got: [u8; 16],
expected: [u8; 16],
},
EncryptedResponseNotPadded {
len: usize,
},
InvalidDhInnerData {
error: tl::errors::DeserializeError,
},
GParameterOutOfRange {
value: BigUint,
low: BigUint,
high: BigUint,
},
DHGenRetry,
DHGenFail,
InvalidAnswerHash {
got: [u8; 20],
expected: [u8; 20],
},
InvalidNewNonceHash {
got: [u8; 16],
expected: [u8; 16],
},
}
impl Error for AuthKeyGenError {}
impl fmt::Display for AuthKeyGenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidResponse { error } => write!(f, "invalid server response: {}", error),
Self::InvalidNonce { got, expected } => {
write!(f, "invalid nonce: got {:?}, expected {:?}", got, expected)
}
Self::InvalidPQSize { size } => write!(f, "invalid pq size {}", size),
Self::UnknownFingerprints { fingerprints } => {
write!(f, "all server fingerprints are unknown: {:?}", fingerprints)
}
Self::DHParamsFail => write!(f, "the generation of DH parameters by the server failed"),
Self::InvalidServerNonce { got, expected } => write!(
f,
"invalid server nonce: got {:?}, expected {:?}",
got, expected
),
Self::EncryptedResponseNotPadded { len } => write!(
f,
"the encrypted server response was {} bytes long, which is not correctly padded",
len
),
Self::InvalidDhInnerData { error } => {
write!(f, "could not deserialize DH inner data: {}", error)
}
Self::GParameterOutOfRange { low, high, value } => write!(
f,
"the parameter g = {} was not in the range {}..{}",
value, low, high
),
Self::DHGenRetry => write!(f, "the generation of DH parameters should be retried"),
Self::DHGenFail => write!(f, "the generation of DH parameters failed"),
Self::InvalidAnswerHash { got, expected } => write!(
f,
"invalid answer hash: got {:?}, expected {:?}",
got, expected
),
Self::InvalidNewNonceHash { got, expected } => write!(
f,
"invalid new nonce hash: got {:?}, expected {:?}",
got, expected
),
}
}
}
impl From<tl::errors::DeserializeError> for AuthKeyGenError {
fn from(error: tl::errors::DeserializeError) -> Self {
Self::InvalidResponse { error }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_rpc_error_parsing() {
assert_eq!(
RpcError::from(tl::types::RpcError {
error_code: 400,
error_message: "CHAT_INVALID".into(),
}),
RpcError {
code: 400,
name: "CHAT_INVALID".into(),
value: None
}
);
assert_eq!(
RpcError::from(tl::types::RpcError {
error_code: 420,
error_message: "FLOOD_WAIT_31".into(),
}),
RpcError {
code: 420,
name: "FLOOD_WAIT".into(),
value: Some(31)
}
);
assert_eq!(
RpcError::from(tl::types::RpcError {
error_code: 500,
error_message: "INTERDC_2_CALL_ERROR".into(),
}),
RpcError {
code: 500,
name: "INTERDC_CALL_ERROR".into(),
value: Some(2)
}
);
}
}