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
// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// ferogram: async Telegram MTProto client in Rust
// https://github.com/ankit-chaubey/ferogram
//
// If you use or modify this code, keep this notice at the top of your file
// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
// https://github.com/ankit-chaubey/ferogram
use std::io;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
/// Async abridged MTProto transport.
#[allow(dead_code)]
pub struct AsyncAbridged {
stream: TcpStream,
/// Whether the 0xef init byte has been sent.
init_sent: bool,
}
#[allow(dead_code)]
impl AsyncAbridged {
pub async fn connect(addr: &str) -> io::Result<Self> {
let stream = TcpStream::connect(addr).await?;
Ok(Self {
stream,
init_sent: false,
})
}
pub async fn send(&mut self, data: &[u8]) -> io::Result<()> {
if !self.init_sent {
self.stream.write_all(&[0xef]).await?;
self.init_sent = true;
}
let words = data.len() / 4;
if words < 0x7f {
self.stream.write_all(&[words as u8]).await?;
} else {
let b0 = 0x7f_u8;
let b1 = (words & 0xff) as u8;
let b2 = ((words >> 8) & 0xff) as u8;
let b3 = ((words >> 16) & 0xff) as u8;
self.stream.write_all(&[b0, b1, b2, b3]).await?;
}
self.stream.write_all(data).await
}
pub async fn recv(&mut self) -> io::Result<Vec<u8>> {
let mut h = [0u8; 1];
self.stream.read_exact(&mut h).await?;
// 0x7f means extended length: next 3 bytes are LE word count.
let words = if h[0] == 0x7f {
let mut b = [0u8; 3];
self.stream.read_exact(&mut b).await?;
b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16
} else {
h[0] as usize
};
let mut buf = vec![0u8; words * 4];
self.stream.read_exact(&mut buf).await?;
// Transport errors arrive as a negative signed LE i32 in the payload.
// Can't detect from the header byte alone (e.g. -404 starts with 0x6C).
// Transport errors are exactly 4 bytes; encrypted frames are 68+ bytes.
if buf.len() == 4 {
let code = i32::from_le_bytes(buf[..4].try_into().unwrap());
if code < 0 {
return Err(io::Error::new(
io::ErrorKind::ConnectionRefused,
format!("transport error from server: {code}"),
));
}
}
Ok(buf)
}
pub fn into_split(
self,
) -> (
tokio::net::tcp::OwnedReadHalf,
tokio::net::tcp::OwnedWriteHalf,
) {
self.stream.into_split()
}
}