ferogram_connect/
envelope.rs1use ferogram_tl_types as tl;
14use ferogram_tl_types::{Cursor, Deserializable};
15
16use crate::error::ConnectError;
17
18const ID_RPC_RESULT: u32 = 0xf35c6d01;
20const ID_RPC_ERROR: u32 = 0x2144ca19;
21const ID_MSG_CONTAINER: u32 = 0x73f1f8dc;
22const ID_GZIP_PACKED: u32 = 0x3072cfa1;
23const ID_MSGS_ACK: u32 = 0x62d6b459;
24const ID_BAD_SERVER_SALT: u32 = 0xedab447b;
25const ID_NEW_SESSION: u32 = 0x9ec20908;
26const ID_BAD_MSG_NOTIFY: u32 = 0xa7eff811;
27const ID_UPDATES: u32 = 0x74ae4240;
28const ID_UPDATE_SHORT: u32 = 0x78d4dec1;
29const ID_UPDATES_COMBINED: u32 = 0x725b04c3;
30const ID_UPDATE_SHORT_MSG: u32 = 0x313bc7f8;
31const ID_UPDATE_SHORT_CHAT_MSG: u32 = 0x4d6deea5;
32const ID_UPDATE_SHORT_SENT_MSG: u32 = 0x9015e101;
33const ID_UPDATES_TOO_LONG: u32 = 0xe317af7e;
34
35pub fn decode_bind_single(body: &[u8]) -> Result<(), String> {
46 const RPC_RESULT: u32 = 0xf35c6d01;
47 const BOOL_TRUE: u32 = 0x9972_75b5;
48 const BOOL_FALSE: u32 = 0xbc79_9737;
49 const RPC_ERROR: u32 = 0x2144_ca19;
50 const BAD_MSG: u32 = 0xa7ef_f811;
51 const BAD_SALT: u32 = 0xedab_447b;
52 const NEW_SESSION: u32 = 0x9ec2_0908; const FUTURE_SALTS: u32 = 0xae50_0895;
54 const MSGS_ACK: u32 = 0x62d6_b459; const PONG: u32 = 0x347_73c5;
56
57 if body.len() < 4 {
58 return Err("skip".to_string());
59 }
60 let ctor = u32::from_le_bytes(body[..4].try_into().unwrap());
61
62 match ctor {
63 BOOL_TRUE => Ok(()),
64
65 BOOL_FALSE => Err("server returned boolFalse (binding rejected)".to_string()),
66
67 NEW_SESSION | FUTURE_SALTS | MSGS_ACK | PONG => Err("skip".to_string()),
69
70 RPC_RESULT if body.len() >= 16 => {
71 let inner = u32::from_le_bytes(body[12..16].try_into().unwrap());
72 match inner {
73 BOOL_TRUE => Ok(()),
74 BOOL_FALSE => Err("rpc_result{boolFalse} (server rejected binding)".to_string()),
75 RPC_ERROR if body.len() >= 20 => {
76 let code = i32::from_le_bytes(body[16..20].try_into().unwrap());
77 let msg = crate::util::tl_read_string(body.get(20..).unwrap_or(&[]))
78 .unwrap_or_default();
79 Err(format!("rpc_error code={code} message={msg:?}"))
80 }
81 _ => Err(format!("rpc_result inner ctor={inner:#010x}")),
82 }
83 }
84
85 BAD_MSG if body.len() >= 16 => {
86 let code = u32::from_le_bytes(body[12..16].try_into().unwrap());
87 let desc = match code {
88 16 => "msg_id too low (clock skew)",
89 17 => "msg_id too high (clock skew)",
90 18 => "incorrect lower 2 bits of msg_id",
91 19 => "duplicate msg_id",
92 20 => "message too old (>300s)",
93 32 => "msg_seqno too low",
94 33 => "msg_seqno too high",
95 34 => "even seqno expected, odd received",
96 35 => "odd seqno expected, even received",
97 48 => "incorrect server salt",
98 64 => "invalid container",
99 _ => "unknown code",
100 };
101 Err(format!("bad_msg_notification code={code} ({desc})"))
102 }
103
104 BAD_SALT if body.len() >= 24 => {
105 let new_salt = i64::from_le_bytes(body[16..24].try_into().unwrap());
106 Err(format!(
107 "bad_server_salt, server wants salt={new_salt:#018x}"
108 ))
109 }
110
111 _ => Err(format!("unknown ctor={ctor:#010x}")),
112 }
113}
114
115pub fn decode_bind_response(body: &[u8]) -> Result<(), String> {
121 const MSG_CONTAINER: u32 = 0x73f1f8dc;
122
123 if body.len() < 4 {
124 return Err(format!("response body too short ({} bytes)", body.len()));
125 }
126 let ctor = u32::from_le_bytes(body[..4].try_into().unwrap());
127
128 if ctor != MSG_CONTAINER {
129 return decode_bind_single(body).map_err(|e| {
131 if e == "skip" {
132 "__need_more__".to_string()
135 } else {
136 e
137 }
138 });
139 }
140
141 if body.len() < 8 {
144 return Err("msg_container too short to read count".to_string());
145 }
146 let count = u32::from_le_bytes(body[4..8].try_into().unwrap()) as usize;
147 let mut pos = 8usize;
148 let mut last_real_err: Option<String> = None;
149
150 for i in 0..count {
151 if pos + 16 > body.len() {
153 return Err(format!(
154 "msg_container truncated at message {i}/{count} (pos={pos} body_len={})",
155 body.len()
156 ));
157 }
158 let msg_bytes = u32::from_le_bytes(body[pos + 12..pos + 16].try_into().unwrap()) as usize;
159 pos += 16;
160
161 if pos + msg_bytes > body.len() {
162 return Err(format!(
163 "msg_container message {i} body overflows (need {msg_bytes}, have {})",
164 body.len() - pos
165 ));
166 }
167 let msg_body = &body[pos..pos + msg_bytes];
168 pos += msg_bytes;
169
170 match decode_bind_single(msg_body) {
171 Ok(()) => return Ok(()), Err(e) if e == "skip" => continue, Err(e) => {
174 last_real_err = Some(e);
177 }
178 }
179 }
180
181 Err(last_real_err.unwrap_or_else(|| "__need_more__".to_string()))
185}
186
187pub enum EnvelopeResult {
188 Payload(Vec<u8>),
189 RawUpdates(Vec<Vec<u8>>),
191 Pts(i32, i32),
193 None,
194}
195
196pub fn unwrap_envelope(body: Vec<u8>) -> Result<EnvelopeResult, ConnectError> {
197 if body.len() < 4 {
198 return Err(ConnectError::other("body < 4 bytes"));
199 }
200 let cid = u32::from_le_bytes(body[..4].try_into().unwrap());
201
202 match cid {
203 ID_RPC_RESULT => {
204 if body.len() < 12 {
205 return Err(ConnectError::other("rpc_result too short"));
206 }
207 unwrap_envelope(body[12..].to_vec())
208 }
209 ID_RPC_ERROR => {
210 if body.len() < 8 {
211 return Err(ConnectError::other("rpc_error too short"));
212 }
213 let code = i32::from_le_bytes(body[4..8].try_into().unwrap());
214 let message = crate::util::tl_read_string(&body[8..]).unwrap_or_default();
215 Err(ConnectError::Rpc { code, message })
216 }
217 ID_MSG_CONTAINER => {
218 if body.len() < 8 {
219 return Err(ConnectError::other("container too short"));
220 }
221 let count = u32::from_le_bytes(body[4..8].try_into().unwrap()) as usize;
222 let mut pos = 8usize;
223 let mut payload: Option<Vec<u8>> = None;
224 let mut raw_updates: Vec<Vec<u8>> = Vec::new();
225
226 for _ in 0..count {
227 if pos + 16 > body.len() { break; }
228 let inner_len = u32::from_le_bytes(body[pos + 12..pos + 16].try_into().unwrap()) as usize;
229 pos += 16;
230 if pos + inner_len > body.len() { break; }
231 let inner = body[pos..pos + inner_len].to_vec();
232 pos += inner_len;
233 match unwrap_envelope(inner)? {
234 EnvelopeResult::Payload(p) => { payload = Some(p); }
235 EnvelopeResult::RawUpdates(mut raws) => { raw_updates.append(&mut raws); }
236 EnvelopeResult::Pts(_, _) => {} EnvelopeResult::None => {}
238 }
239 }
240 if let Some(p) = payload {
241 Ok(EnvelopeResult::Payload(p))
242 } else if !raw_updates.is_empty() {
243 Ok(EnvelopeResult::RawUpdates(raw_updates))
244 } else {
245 Ok(EnvelopeResult::None)
246 }
247 }
248 ID_GZIP_PACKED => {
249 let bytes = crate::util::tl_read_bytes(&body[4..]).unwrap_or_default();
250 unwrap_envelope(crate::util::gz_inflate(&bytes)?)
251 }
252 ID_MSGS_ACK | ID_NEW_SESSION | ID_BAD_SERVER_SALT | ID_BAD_MSG_NOTIFY
257 | 0xd33b5459 | 0x04deb57d | 0x8cc0d131 | 0x276d3ec6 | 0x809db6df | 0x7d861a08 | 0x0949d9dc | 0xae500895 | 0x9299359f | 0xe22045fc | 0x62d350c9 => {
270 Ok(EnvelopeResult::None)
271 }
272 ID_UPDATES | ID_UPDATE_SHORT | ID_UPDATES_COMBINED
278 | ID_UPDATE_SHORT_MSG | ID_UPDATE_SHORT_CHAT_MSG
279 | ID_UPDATES_TOO_LONG => {
280 Ok(EnvelopeResult::RawUpdates(vec![body]))
281 }
282 ID_UPDATE_SHORT_SENT_MSG => {
285 let mut cur = Cursor::from_slice(&body[4..]);
286 match tl::types::UpdateShortSentMessage::deserialize(&mut cur) {
287 Ok(m) => {
288 tracing::debug!(
289 "[ferogram] updateShortSentMessage (RPC): pts={} pts_count={}: advancing pts",
290 m.pts, m.pts_count
291 );
292 Ok(EnvelopeResult::Pts(m.pts, m.pts_count))
293 }
294 Err(e) => {
295 tracing::debug!("[ferogram] updateShortSentMessage deserialize error: {e}");
296 Ok(EnvelopeResult::None)
297 }
298 }
299 }
300 _ => Ok(EnvelopeResult::Payload(body)),
301 }
302}
303
304pub fn updates_entities(
310 updates: &tl::enums::Updates,
311) -> (Vec<tl::enums::User>, Vec<tl::enums::Chat>) {
312 match updates {
313 tl::enums::Updates::Updates(u) => (u.users.clone(), u.chats.clone()),
314 tl::enums::Updates::Combined(u) => (u.users.clone(), u.chats.clone()),
315 _ => (Vec::new(), Vec::new()),
316 }
317}
318
319pub fn chat_to_peer(chat: &tl::enums::Chat) -> Option<tl::enums::Peer> {
321 match chat {
322 tl::enums::Chat::Channel(c) => Some(tl::enums::Peer::Channel(tl::types::PeerChannel {
323 channel_id: c.id,
324 })),
325 tl::enums::Chat::ChannelForbidden(c) => {
326 Some(tl::enums::Peer::Channel(tl::types::PeerChannel {
327 channel_id: c.id,
328 }))
329 }
330 tl::enums::Chat::Chat(c) => {
331 Some(tl::enums::Peer::Chat(tl::types::PeerChat { chat_id: c.id }))
332 }
333 tl::enums::Chat::Forbidden(c) => {
334 Some(tl::enums::Peer::Chat(tl::types::PeerChat { chat_id: c.id }))
335 }
336 tl::enums::Chat::Empty(_) => None,
337 }
338}