1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
// Copyright 2020 - developers of the `grammers` project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of the [Mobile Transport Protocol]. This layer is
//! responsible for converting zero or more input requests into outgoing
//! messages, and to process the response.
//!
//! A distinction between plain and encrypted is made for simplicity (the
//! plain hardly requires to process any state) and to help prevent invalid
//! states (encrypted communication cannot be made without an authorization
//! key).
//!
//! [Mobile Transport Protocol]: https://core.telegram.org/mtproto/description
mod encrypted;
mod plain;
use crate::MsgId;
use crypto::RingBuffer;
pub use encrypted::{
Encrypted, ENCRYPTED_PACKET_HEADER_LEN, MAX_TRANSPORT_HEADER_LEN, MESSAGE_CONTAINER_HEADER_LEN,
PLAIN_PACKET_HEADER_LEN,
};
use grammers_crypto as crypto;
use grammers_tl_types as tl;
pub use plain::Plain;
use std::fmt;
/// Results from the deserialization of a response.
pub struct Deserialization {
/// Result bodies to Remote Procedure Calls.
pub rpc_results: Vec<(MsgId, Result<Vec<u8>, RequestError>)>,
/// Updates that came in the response.
pub updates: Vec<Vec<u8>>,
}
/// The error type for the deserialization of server messages.
#[derive(Clone, Debug, PartialEq)]
pub enum DeserializeError {
/// The server's authorization key did not match our expectations.
BadAuthKey { got: i64, expected: i64 },
/// The server's message ID did not match our expectations.
BadMessageId { got: i64 },
/// The server's message length was not strictly positive.
NegativeMessageLength { got: i32 },
/// The server's message length was past the buffer.
TooLongMessageLength { got: usize, max_length: usize },
/// The error occured at the [transport level], making it impossible to
/// deserialize any data. The absolute value indicates the HTTP error
/// code. Some known, possible codes are:
///
/// * 404, if the authorization key used was not found, meaning that the
/// server is not aware of the key used by the client, so it cannot be
/// used to securely communicate with it.
///
/// * 429, if too many transport connections are established to the same
/// IP address in a too-short lapse of time.
///
/// [transport level]: https://core.telegram.org/mtproto/mtproto-transports#transport-errors
TransportError { code: i32 },
/// The received buffer is too small to contain a valid response message,
/// or the response seemed valid at first but trying to deserialize it
/// proved the buffer to be too small.
MessageBufferTooSmall,
/// The server responded with compressed data which we failed to decompress.
DecompressionFailed,
/// While deserializing the response types one of them had a constructor
/// that did not match our expectations. The invalid ID is contained
/// within this variant.
UnexpectedConstructor { id: u32 },
/// Attempting to decrypt the message failed in some way.
DecryptionError(crypto::Error),
}
impl std::error::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::deserialize::Error> for DeserializeError {
fn from(error: tl::deserialize::Error) -> Self {
use tl::deserialize::Error as Err;
match error {
Err::UnexpectedEof => DeserializeError::MessageBufferTooSmall,
Err::UnexpectedConstructor { id } => DeserializeError::UnexpectedConstructor { id },
}
}
}
impl From<crypto::Error> for DeserializeError {
fn from(error: crypto::Error) -> Self {
Self::DecryptionError(error)
}
}
/// The error type reported by the server when a request is misused.
#[derive(Clone, Debug, PartialEq)]
pub struct RpcError {
/// A numerical value similar to HTTP status codes.
pub code: i32,
/// The ASCII error name, normally in screaming snake case.
pub name: String,
/// If the error contained an additional value, it will be present here.
pub value: Option<u32>,
/// The constructor identifier of the request that triggered this error.
/// Won't be present if the error was artificially constructed.
pub caused_by: Option<u32>,
}
impl std::error::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(caused_by) = self.caused_by {
write!(f, " caused by {}", tl::name_for_id(caused_by))?;
}
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 {
// Extract the numeric value in the error, if any
if let Some(value) = error
.error_message
.split(|c: char| !c.is_ascii_digit())
.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, ""),
// Safe to unwrap, matched on digits
value: Some(value.parse().unwrap()),
caused_by: None,
}
} else {
Self {
code: error.error_code,
name: error.error_message.clone(),
value: None,
caused_by: None,
}
}
}
}
impl RpcError {
/// Matches on the name of the RPC error (case-sensitive).
///
/// Useful in `match` arm guards. A single trailing or leading asterisk (`'*'`) is allowed,
/// and will instead check if the error name starts (or ends with) the input parameter.
///
/// # Examples
///
/// ```
/// # let request_result = Result::<(), _>::Err(grammers_mtproto::mtp::RpcError {
/// # code: 400, name: "PHONE_CODE_INVALID".to_string(), value: None, caused_by: None });
/// #
/// match request_result {
/// Err(rpc_err) if rpc_err.is("SESSION_PASSWORD_NEEDED") => panic!(),
/// Err(rpc_err) if rpc_err.is("PHONE_CODE_*") => {},
/// _ => panic!()
/// }
/// ```
pub fn is(&self, rpc_error: &str) -> bool {
if let Some(rpc_error) = rpc_error.strip_suffix('*') {
self.name.starts_with(rpc_error)
} else if let Some(rpc_error) = rpc_error.strip_prefix('*') {
self.name.ends_with(rpc_error)
} else {
self.name == rpc_error
}
}
}
/// This error occurs when a Remote Procedure call was unsuccessful.
///
/// The request should be retransmited when this happens, unless the
/// variant is `InvalidParameters`.
#[derive(Clone, Debug, PartialEq)]
pub enum RequestError {
/// The parameters used in the request were invalid and caused a
/// Remote Procedure Call error.
RpcError(RpcError),
/// The call was dropped (cancelled), so the server will not process it.
Dropped,
/// The message sent to the server was invalid, and the request
/// must be retransmitted.
BadMessage {
/// The code of the bad message error.
code: i32,
},
/// The deserialization of the response that was meant to confirm this
/// request failed, so while the server technically responded to the
/// request its answer is useless as it could not be understood properly.
Deserialize(DeserializeError),
}
impl std::error::Error for RequestError {}
impl fmt::Display for RequestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RpcError(error) => write!(f, "request error: {}", error),
Self::Dropped => write!(f, "request error: request dropped"),
Self::BadMessage { code } => write!(f, "request error: bad message (code {}, {})", code, match code {
16 => "msg_id too low",
17 => "msg_id too high",
18 => "incorrect two lower order msg_id bits; this is a bug",
19 => "container msg_id is the same as msg_id of a previously received message; this is a bug",
20 => "message too old",
32 => "msg_seqno too low",
33 => "msg_seqno too high",
34 => "an even msg_seqno expected; this may be a bug",
35 => "odd msg_seqno expected; this may be a bug",
48 => "incorrect server salt",
64 => "invalid container; this is likely a bug",
_ => "unknown explanation; please report this issue",
}),
Self::Deserialize(error) => write!(f, "request error: {}", error),
}
}
}
impl From<DeserializeError> for RequestError {
fn from(error: DeserializeError) -> Self {
Self::Deserialize(error)
}
}
impl From<tl::deserialize::Error> for RequestError {
fn from(error: tl::deserialize::Error) -> Self {
RequestError::from(DeserializeError::from(error))
}
}
/// The trait used by the [Mobile Transport Protocol] to serialize outgoing
/// messages and deserialize incoming ones into proper responses.
///
/// [Mobile Transport Protocol]: https://core.telegram.org/mtproto/description
pub trait Mtp {
/// Serializes one request to the input buffer.
/// The same buffer should be used until `finalize` is called.
///
/// Returns the message ID assigned the request if it was serialized, or `None` if the buffer
/// is full and cannot hold more requests.
///
/// # Panics
///
/// The method panics if the body length is not padded to 4 bytes. The
/// serialization of requests will always be correctly padded, so adding
/// an error case for this rare case (impossible with the expected inputs)
/// would simply be unnecessary.
///
/// The method also panics if the body length is too large for similar
/// reasons. It is not reasonable to construct huge requests (although
/// possible) because they would likely fail with a RPC error anyway,
/// so we avoid another error case by simply panicking.
///
/// The definition of "too large" is roughly 1MB, so as long as the
/// payload is below that mark, it's safe to call.
fn push(&mut self, buffer: &mut RingBuffer<u8>, request: &[u8]) -> Option<MsgId>;
/// Finalizes the buffer of requests.
///
/// Note that even if there are no requests to serialize, the protocol may
/// produce data that has to be sent after deserializing incoming messages.
///
/// The buffer may remain empty if there are no actions to take.
fn finalize(&mut self, buffer: &mut RingBuffer<u8>);
/// Deserializes a single incoming message payload into zero or more responses.
fn deserialize(&mut self, payload: &[u8]) -> Result<Deserialization, DeserializeError>;
/// Reset the state, as if a new instance was just created.
fn reset(&mut self);
}
#[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,
caused_by: 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),
caused_by: None,
}
);
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),
caused_by: None,
}
);
}
}