1use ferogram_connect::FrameKind;
16use ferogram_mtproto::{
17 EncryptedSession, SeenMsgIds, Session, authentication as auth, new_seen_msg_ids, step2_temp,
18};
19use ferogram_tl_types as tl;
20use ferogram_tl_types::{Cursor, Deserializable, RemoteCall};
21use tokio::io::AsyncReadExt;
22use tokio::net::TcpStream;
23
24use crate::errors::InvocationError;
25use crate::pool::{build_msgs_ack_body, build_msgs_ack_ping_body};
26use ferogram_connect::TransportKind;
27
28const PENDING_ACKS_THRESHOLD: usize = 10;
31
32const PING_EVERY_N_CHUNKS: u32 = 5;
35
36pub struct DcConnection {
37 stream: TcpStream,
38 enc: EncryptedSession,
39 pending_acks: Vec<i64>,
40 call_count: u32,
41 frame_kind: FrameKind,
43 #[allow(dead_code)]
45 seen_msg_ids: SeenMsgIds,
46}
47
48impl DcConnection {
49 #[tracing::instrument(skip(socks5), fields(addr = %addr, dc_id = dc_id))]
52 pub async fn connect_fastest(
53 addr: &str,
54 socks5: Option<&ferogram_connect::Socks5Config>,
55 dc_id: i16,
56 ) -> Result<(Self, String), InvocationError> {
57 let race = ferogram_connect::default_transport_race();
58 Self::connect_fastest_with(addr, socks5, dc_id, &race).await
59 }
60
61 #[tracing::instrument(skip(socks5, race), fields(addr = %addr, dc_id = dc_id))]
64 pub async fn connect_fastest_with(
65 addr: &str,
66 socks5: Option<&ferogram_connect::Socks5Config>,
67 dc_id: i16,
68 race: &[ferogram_connect::RaceLeg],
69 ) -> Result<(Self, String), InvocationError> {
70 use tokio::task::JoinSet;
71 let addr = addr.to_owned();
72 let socks5 = socks5.cloned();
73 tracing::debug!(
74 "[ferogram::sender] probing {addr} with {} transports in parallel: {:?}",
75 race.len(),
76 race.iter().map(|l| &l.transport).collect::<Vec<_>>()
77 );
78 let mut set: JoinSet<Result<(DcConnection, String), InvocationError>> = JoinSet::new();
79
80 for leg in race {
81 let a = addr.clone();
82 let s = socks5.clone();
83 let transport = leg.transport.clone();
84 let stagger = leg.stagger;
85 let label = format!("{transport:?}");
86 set.spawn(async move {
87 if !stagger.is_zero() {
88 tokio::time::sleep(stagger).await;
89 }
90 Ok((
91 DcConnection::connect_raw(&a, s.as_ref(), None, &transport, dc_id).await?,
92 label,
93 ))
94 });
95 }
96
97 let mut last_err = InvocationError::Deserialize("connect_fastest: no candidates".into());
98 while let Some(outcome) = set.join_next().await {
99 match outcome {
100 Ok(Ok((conn, label))) => {
101 set.abort_all();
102 return Ok((conn, label));
103 }
104 Ok(Err(e)) => {
105 last_err = e;
106 }
107 Err(e) if e.is_cancelled() => {}
108 Err(_) => {}
109 }
110 }
111 Err(last_err)
112 }
113
114 #[tracing::instrument(skip(socks5, mtproxy, transport), fields(addr = %addr, dc_id = dc_id))]
116 pub async fn connect_raw(
117 addr: &str,
118 socks5: Option<&ferogram_connect::Socks5Config>,
119 mtproxy: Option<&ferogram_connect::MtProxyConfig>,
120 transport: &TransportKind,
121 dc_id: i16,
122 ) -> Result<Self, InvocationError> {
123 tracing::debug!("[ferogram::sender] connecting to {addr} with known auth key");
124 let (stream, frame_kind, enc) =
125 ferogram_connect::connect_to_dc(addr, dc_id, transport, socks5, mtproxy).await?;
126
127 tracing::debug!("[ferogram::sender] DH complete, auth key established for {addr}");
128 let seen = new_seen_msg_ids();
129 Ok(Self {
130 stream,
131 frame_kind,
132 enc: EncryptedSession::with_seen(
133 enc.auth_key_bytes(),
134 enc.salt,
135 enc.time_offset,
136 seen.clone(),
137 ),
138 pending_acks: Vec::new(),
139 call_count: 0,
140 seen_msg_ids: seen,
141 })
142 }
143
144 #[allow(clippy::too_many_arguments)]
147 pub async fn connect_with_key(
148 addr: &str,
149 auth_key: [u8; 256],
150 first_salt: i64,
151 time_offset: i32,
152 socks5: Option<&ferogram_connect::Socks5Config>,
153 mtproxy: Option<&ferogram_connect::MtProxyConfig>,
154 transport: &TransportKind,
155 dc_id: i16,
156 pfs: bool,
157 ) -> Result<Self, InvocationError> {
158 let (mut stream, mut frame_kind) =
160 ferogram_connect::Connection::open_stream_pub(addr, dc_id, transport, socks5, mtproxy)
161 .await?;
162
163 if pfs {
164 tracing::debug!("[ferogram::sender] PFS: binding temporary key for DC{dc_id}");
165 match Self::do_pool_pfs_bind(&mut stream, &mut frame_kind, &auth_key, dc_id).await {
166 Ok(temp_enc) => {
167 tracing::debug!("[ferogram::sender] PFS: temporary key bound for DC{dc_id}");
168 return Ok(Self {
169 stream,
170 frame_kind,
171 enc: temp_enc,
172 pending_acks: Vec::new(),
173 call_count: 0,
174 seen_msg_ids: new_seen_msg_ids(),
175 });
176 }
177 Err(e) => {
178 tracing::warn!(
179 "[ferogram::sender] PFS bind failed for DC{dc_id} ({e}); using permanent key"
180 );
181 return Err(e);
182 }
183 }
184 }
185
186 let seen = new_seen_msg_ids();
187 Ok(Self {
188 stream,
189 frame_kind,
190 enc: EncryptedSession::with_seen(auth_key, first_salt, time_offset, seen.clone()),
191 pending_acks: Vec::new(),
192 call_count: 0,
193 seen_msg_ids: seen,
194 })
195 }
196
197 async fn do_pool_pfs_bind(
199 stream: &mut tokio::net::TcpStream,
200 kind: &mut FrameKind,
201 perm_auth_key: &[u8; 256],
202 dc_id: i16,
203 ) -> Result<EncryptedSession, InvocationError> {
204 use ferogram_mtproto::{
205 auth_key_id_from_key, encrypt_bind_inner, gen_msg_id, new_seen_msg_ids,
206 serialize_bind_temp_auth_key,
207 };
208 const TEMP_EXPIRES: i32 = 86_400; let mut plain = Session::new();
212
213 let (req1, s1) = auth::step1().map_err(|e| InvocationError::Deserialize(e.to_string()))?;
214 Self::send_plain_frame(stream, &plain.pack(&req1).to_plaintext_bytes(), kind).await?;
215 let res_pq: tl::enums::ResPq = Self::recv_plain_frame(stream, kind).await?;
216
217 let (req2, s2) = step2_temp(s1, res_pq, dc_id as i32, TEMP_EXPIRES)
218 .map_err(|e| InvocationError::Deserialize(e.to_string()))?;
219 Self::send_plain_frame(stream, &plain.pack(&req2).to_plaintext_bytes(), kind).await?;
220 let dh: tl::enums::ServerDhParams = Self::recv_plain_frame(stream, kind).await?;
221
222 let (req3, s3) =
223 auth::step3(s2, dh).map_err(|e| InvocationError::Deserialize(e.to_string()))?;
224 Self::send_plain_frame(stream, &plain.pack(&req3).to_plaintext_bytes(), kind).await?;
225 let ans: tl::enums::SetClientDhParamsAnswer = Self::recv_plain_frame(stream, kind).await?;
226
227 let done = {
228 let mut result =
229 auth::finish(s3, ans).map_err(|e| InvocationError::Deserialize(e.to_string()))?;
230 let mut attempts = 0u8;
231 loop {
232 match result {
233 ferogram_mtproto::FinishResult::Done(d) => break d,
234 ferogram_mtproto::FinishResult::Retry {
235 retry_id,
236 dh_params,
237 nonce,
238 server_nonce,
239 new_nonce,
240 } => {
241 attempts += 1;
242 if attempts >= 5 {
243 return Err(InvocationError::Deserialize(
244 "PFS pool temp DH retry exceeded 5".into(),
245 ));
246 }
247 let (rr, s3r) = ferogram_mtproto::retry_step3(
248 &dh_params,
249 nonce,
250 server_nonce,
251 new_nonce,
252 retry_id,
253 )
254 .map_err(|e| InvocationError::Deserialize(e.to_string()))?;
255 Self::send_plain_frame(stream, &plain.pack(&rr).to_plaintext_bytes(), kind)
256 .await?;
257 let ar: tl::enums::SetClientDhParamsAnswer =
258 Self::recv_plain_frame(stream, kind).await?;
259 result = auth::finish(s3r, ar)
260 .map_err(|e| InvocationError::Deserialize(e.to_string()))?;
261 }
262 }
263 }
264 };
265
266 let temp_key = done.auth_key;
267 let temp_salt = done.first_salt;
268 let temp_offset = done.time_offset;
269
270 let temp_key_id = auth_key_id_from_key(&temp_key);
272 let perm_key_id = auth_key_id_from_key(perm_auth_key);
273
274 let mut nonce_buf = [0u8; 8];
275 ferogram_crypto::fill_random(&mut nonce_buf);
276 let nonce = i64::from_le_bytes(nonce_buf);
277
278 let server_now = std::time::SystemTime::now()
279 .duration_since(std::time::UNIX_EPOCH)
280 .expect("system clock is before UNIX epoch")
281 .as_secs() as i32
282 + temp_offset;
283 let expires_at = server_now + TEMP_EXPIRES;
284
285 let seen = new_seen_msg_ids();
286 let mut temp_enc = EncryptedSession::with_seen(temp_key, temp_salt, temp_offset, seen);
287 let temp_session_id = temp_enc.session_id();
288
289 let msg_id = gen_msg_id();
290 let enc_msg = encrypt_bind_inner(
291 perm_auth_key,
292 msg_id,
293 nonce,
294 temp_key_id,
295 perm_key_id,
296 temp_session_id,
297 expires_at,
298 );
299 let bind_body = serialize_bind_temp_auth_key(perm_key_id, nonce, expires_at, &enc_msg);
300
301 let wire = temp_enc.pack_body_at_msg_id(&bind_body, msg_id);
303 Self::send_abridged(stream, &wire, kind).await?;
304
305 for attempt in 0u8..5 {
309 let mut raw = Self::recv_abridged(stream, kind).await?;
310 let decrypted = temp_enc.unpack(&mut raw).map_err(|e| {
311 InvocationError::Deserialize(format!("PFS pool bind decrypt: {e:?}"))
312 })?;
313 match ferogram_connect::decode_bind_response(&decrypted.body) {
314 Ok(()) => {
315 return Ok(temp_enc);
319 }
320 Err(ref e) if e == "__need_more__" => {
321 tracing::debug!(
322 "[ferogram::sender] PFS (DC{dc_id}): got informational frame on attempt {attempt}, reading next"
323 );
324 continue;
325 }
326 Err(reason) => {
327 tracing::error!(
328 "[ferogram::sender] PFS bind rejected by server for DC{dc_id}: {reason}"
329 );
330 return Err(InvocationError::Deserialize(format!(
331 "auth.bindTempAuthKey (pool): {reason}"
332 )));
333 }
334 }
335 }
336 Err(InvocationError::Deserialize(
337 "auth.bindTempAuthKey (pool): no boolTrue after 5 frames".into(),
338 ))
339 }
340
341 pub fn auth_key_bytes(&self) -> [u8; 256] {
346 self.enc.auth_key_bytes()
347 }
348 pub fn first_salt(&self) -> i64 {
350 self.enc.salt
351 }
352 pub fn time_offset(&self) -> i32 {
354 self.enc.time_offset
355 }
356
357 pub fn into_parts(self) -> (TcpStream, FrameKind, EncryptedSession) {
367 (self.stream, self.frame_kind, self.enc)
368 }
369
370 #[tracing::instrument(skip(self, req), fields(method = std::any::type_name::<R>()))]
377 pub async fn rpc_call<R: RemoteCall>(&mut self, req: &R) -> Result<Vec<u8>, InvocationError> {
378 let _t0 = std::time::Instant::now();
379 self.call_count += 1;
382 if self.call_count.is_multiple_of(PING_EVERY_N_CHUNKS) {
383 let ping_id = self.call_count as i64;
384 let ping_body = build_msgs_ack_ping_body(ping_id);
385 let (ping_wire, _) = self.enc.pack_body_with_msg_id(&ping_body, true);
387 let _ = Self::send_abridged(&mut self.stream, &ping_wire, &mut self.frame_kind).await;
395 }
396
397 if !self.pending_acks.is_empty() {
399 let ack_body = build_msgs_ack_body(&self.pending_acks);
400 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
401 let _ = Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind).await;
402 self.pending_acks.clear();
403 }
404
405 let (wire, mut sent_msg_id) = self.enc.pack_with_msg_id(req);
407 Self::send_abridged(&mut self.stream, &wire, &mut self.frame_kind).await?;
408 let mut salt_retries = 0u8;
409 let mut session_resets = 0u8;
410 loop {
411 let mut raw = Self::recv_abridged(&mut self.stream, &mut self.frame_kind).await?;
412 let msg = self
413 .enc
414 .unpack(&mut raw)
415 .map_err(|e| InvocationError::Deserialize(e.to_string()))?;
416 self.pending_acks.push(msg.msg_id);
418 if self.pending_acks.len() >= PENDING_ACKS_THRESHOLD {
419 let ack_body = build_msgs_ack_body(&self.pending_acks);
422 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
423 let _ =
424 Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind).await;
425 self.pending_acks.clear();
426 }
427 if msg.body.len() < 4 {
429 return Ok(msg.body);
430 }
431 let mut need_resend = false;
432 let mut need_session_reset = false;
433 let mut bad_msg_code: Option<u32> = None;
434 let mut bad_msg_server_id: Option<i64> = None;
435 let scan_result = Self::scan_body(
438 &msg.body,
439 &mut self.enc.salt,
440 &mut need_resend,
441 &mut need_session_reset,
442 &mut bad_msg_code,
443 &mut bad_msg_server_id,
444 Some(sent_msg_id),
445 msg.msg_id,
446 )?;
447 if need_session_reset {
449 session_resets += 1;
450 if session_resets > 2 {
451 return Err(InvocationError::Deserialize(
452 "new_session_created: exceeded 2 resets".into(),
453 ));
454 }
455 if !self.pending_acks.is_empty() {
456 let ack_body = build_msgs_ack_body(&self.pending_acks);
457 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
458 let _ = Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind)
459 .await;
460 self.pending_acks.clear();
461 }
462 if scan_result.is_none() {
466 tracing::debug!(
468 "[ferogram::sender] new_session_created: resending request (attempt {session_resets}/2)"
469 );
470 let (wire, new_id) = self.enc.pack_with_msg_id(req);
471 sent_msg_id = new_id;
472 Self::send_abridged(&mut self.stream, &wire, &mut self.frame_kind).await?;
473 }
474 } else if need_resend {
478 match bad_msg_code {
480 Some(16) | Some(17) => {
481 if let Some(srv_id) = bad_msg_server_id {
482 self.enc.correct_time_offset(srv_id);
483 }
484 }
489 Some(32) | Some(33) => {
490 self.enc
493 .correct_seq_no(bad_msg_code.expect("matched Some arm"));
494 }
495 _ => {
496 self.enc.undo_seq_no();
498 }
499 }
500 salt_retries += 1;
501 if salt_retries >= 5 {
502 return Err(InvocationError::Deserialize(
503 "bad_server_salt/bad_msg: exceeded 5 retries".into(),
504 ));
505 }
506 tracing::debug!(
507 "[ferogram::sender] resending transfer request after bad_msg correction (code={bad_msg_code:?}, attempt {salt_retries}/5)"
508 );
509 if !self.pending_acks.is_empty() {
510 let ack_body = build_msgs_ack_body(&self.pending_acks);
511 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
512 let _ = Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind)
513 .await;
514 self.pending_acks.clear();
515 }
516 let (wire, new_id) = self.enc.pack_with_msg_id(req);
517 sent_msg_id = new_id;
518 Self::send_abridged(&mut self.stream, &wire, &mut self.frame_kind).await?;
519 }
520 if let Some(result) = scan_result {
521 crate::metrics_shim::counter!("ferogram.rpc_calls_total", "result" => "ok")
522 .increment(1);
523 crate::metrics_shim::histogram!("ferogram.rpc_latency_ms")
524 .record(_t0.elapsed().as_millis() as f64);
525 return Ok(result);
526 }
527 }
528 }
529 #[allow(clippy::too_many_arguments)]
542 fn scan_body(
543 body: &[u8],
544 salt: &mut i64,
545 need_resend: &mut bool,
546 need_session_reset: &mut bool,
547 bad_msg_code: &mut Option<u32>,
548 bad_msg_server_id: &mut Option<i64>,
549 sent_msg_id: Option<i64>,
550 server_msg_id: i64,
551 ) -> Result<Option<Vec<u8>>, InvocationError> {
552 if body.len() < 4 {
553 return Ok(None);
554 }
555 let cid = u32::from_le_bytes(body[..4].try_into().expect("body.len() >= 4 checked above"));
556 match cid {
557 0xf35c6d01 => {
558 if body.len() >= 12
559 && let Some(expected) = sent_msg_id {
560 let resp_id = i64::from_le_bytes(body[4..12].try_into().expect("body.len() >= 12 checked above"));
561 if resp_id != expected {
562 tracing::debug!(
563 "[ferogram::sender] rpc_result msg_id mismatch (got {resp_id:#018x}, want {expected:#018x}); skipping this frame"
564 );
565 return Ok(None);
566 }
567 }
568 let inner = if body.len() >= 12 { &body[12..] } else { body };
569 if inner.len() >= 4
571 && u32::from_le_bytes(inner[..4].try_into().expect("inner.len() >= 4 checked above")) == 0x3072cfa1
572 {
573 let mut dummy_salt = *salt;
574 let mut nr = false; let mut nsr = false;
575 let mut bc = None; let mut bsi = None;
576 if let Some(r) = Self::scan_body(inner, &mut dummy_salt, &mut nr, &mut nsr, &mut bc, &mut bsi, None, server_msg_id)? {
577 return Ok(Some(r));
578 }
579 if let Some(compressed) = ferogram_connect::tl_read_bytes(&inner[4..])
581 && let Ok(out) = ferogram_connect::gz_inflate(&compressed)
582 {
583 return Ok(Some(out));
584 }
585 return Ok(None);
586 }
587 if inner.len() >= 8
588 && u32::from_le_bytes(inner[..4].try_into().expect("inner.len() >= 8 checked above")) == 0x2144ca19
589 {
590 let code = i32::from_le_bytes(inner[4..8].try_into().expect("inner.len() >= 8 checked above"));
591 let message = ferogram_connect::tl_read_string(&inner[8..]).unwrap_or_default();
592 return Err(InvocationError::Rpc(
593 crate::errors::RpcError::from_telegram(code, &message),
594 ));
595 }
596 Ok(Some(inner.to_vec()))
597 }
598 0x2144ca19 => {
599 if body.len() < 8 {
600 return Err(InvocationError::Deserialize("rpc_error short".into()));
601 }
602 let code = i32::from_le_bytes(body[4..8].try_into().expect("body.len() >= 8 checked above"));
603 let message = ferogram_connect::tl_read_string(&body[8..]).unwrap_or_default();
604 Err(InvocationError::Rpc(crate::errors::RpcError::from_telegram(code, &message)))
605 }
606 0xedab447b => {
607 if body.len() >= 28 {
609 let bad_msg_id = i64::from_le_bytes(body[4..12].try_into().expect("body.len() >= 28 checked above"));
610 let new_salt = i64::from_le_bytes(body[20..28].try_into().expect("body.len() >= 28 checked above"));
611 if sent_msg_id.is_none_or(|id| id == bad_msg_id) {
614 *salt = new_salt;
615 *need_resend = true;
616 }
617 }
618 Ok(None)
619 }
620 0x9ec20908 => {
621 if body.len() >= 28 {
624 let first_msg_id = i64::from_le_bytes(body[4..12].try_into().expect("body.len() >= 28 checked above"));
625 let unique_id = i64::from_le_bytes(body[12..20].try_into().expect("body.len() >= 28 checked above"));
626 let server_salt = i64::from_le_bytes(body[20..28].try_into().expect("body.len() >= 28 checked above"));
627 tracing::debug!(
628 unique_id = format_args!("{unique_id:#018x}"),
629 first_msg_id,
630 salt = server_salt,
631 "[ferogram::sender] new_session_created: server opened fresh session"
632 );
633 *salt = server_salt;
634 if sent_msg_id.is_some_and(|id| id < first_msg_id) {
640 *need_session_reset = true;
641 }
642 }
643 Ok(None)
644 }
645 0xa7eff811 => {
646 if body.len() >= 20 {
652 let bad_msg_id = i64::from_le_bytes(body[4..12].try_into().expect("body.len() >= 20 checked above"));
653 let error_code = u32::from_le_bytes(body[16..20].try_into().expect("body.len() >= 20 checked above"));
655 tracing::debug!(
656 bad_msg_id = format_args!("{bad_msg_id:#018x}"),
657 error_code,
658 "[ferogram::sender] bad_msg_notification received"
659 );
660 match error_code {
661 16 | 17 => {
662 *bad_msg_code = Some(error_code);
666 *bad_msg_server_id = Some(server_msg_id);
667 *need_resend = sent_msg_id.is_none_or(|id| id == bad_msg_id);
668 }
669 32 | 33 => {
670 *bad_msg_code = Some(error_code);
672 *need_resend = sent_msg_id.is_none_or(|id| id == bad_msg_id);
673 }
674 48 => {
675 *need_resend = sent_msg_id.is_none_or(|id| id == bad_msg_id);
682 tracing::debug!(
683 "[ferogram::sender] bad_msg code 48 (wrong server salt): will resend with updated salt"
684 );
685 }
686 _ => {
687 *need_resend = sent_msg_id.is_none_or(|id| id == bad_msg_id);
689 }
690 }
691 }
692 Ok(None)
693 }
694 0x347773c5 => {
695 if body.len() >= 12
701 && let Some(expected) = sent_msg_id
702 {
703 let pong_req_id = i64::from_le_bytes(body[4..12].try_into().expect("body.len() >= 12 for pong"));
704 if pong_req_id == expected {
705 return Ok(Some(body.to_vec()));
706 }
707 }
708 Ok(None)
710 }
711 0x73f1f8dc => {
712 if body.len() < 8 {
713 return Ok(None);
714 }
715 let count = u32::from_le_bytes(body[4..8].try_into().expect("body.len() >= 8 for msg_container")) as usize;
716 let mut pos = 8usize;
717 let mut found: Option<Vec<u8>> = None;
720 for _ in 0..count {
721 if pos + 16 > body.len() { break; }
722 let inner_bytes =
723 u32::from_le_bytes(body[pos + 12..pos + 16].try_into().expect("pos+16 <= body.len() checked above")) as usize;
724 pos += 16;
725 if pos + inner_bytes > body.len() { break; }
726 let inner = &body[pos..pos + inner_bytes];
727 pos += inner_bytes;
728 if found.is_none() {
729 if let Some(r) = Self::scan_body(inner, salt, need_resend,
730 need_session_reset, bad_msg_code, bad_msg_server_id, sent_msg_id,
731 server_msg_id)?
732 {
733 found = Some(r);
734 }
737 } else {
738 let _ = Self::scan_body(inner, salt, need_resend, need_session_reset,
744 bad_msg_code, bad_msg_server_id, sent_msg_id,
745 server_msg_id)?;
746 }
747 }
748 Ok(found)
749 }
750 0x3072cfa1 => {
751 if let Some(compressed) = ferogram_connect::tl_read_bytes(&body[4..])
753 && let Ok(decompressed) = ferogram_connect::gz_inflate(&compressed)
754 && !decompressed.is_empty()
755 {
756 return Self::scan_body(
757 &decompressed, salt,
758 need_resend, need_session_reset,
759 bad_msg_code, bad_msg_server_id,
760 sent_msg_id,
761 server_msg_id,
762 );
763 }
764 Ok(None)
765 }
766 _ => Ok(None),
767 }
768 }
769
770 pub async fn rpc_call_serializable<S: ferogram_tl_types::Serializable>(
772 &mut self,
773 req: &S,
774 ) -> Result<Vec<u8>, InvocationError> {
775 if !self.pending_acks.is_empty() {
776 let ack_body = build_msgs_ack_body(&self.pending_acks);
777 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
778 let _ = Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind).await;
779 self.pending_acks.clear();
780 }
781 let (wire, mut sent_msg_id) = self.enc.pack_serializable_with_msg_id(req);
782 Self::send_abridged(&mut self.stream, &wire, &mut self.frame_kind).await?;
783 let mut salt_retries = 0u8;
784 let mut session_resets = 0u8;
785 loop {
786 let mut raw = Self::recv_abridged(&mut self.stream, &mut self.frame_kind).await?;
787 let msg = self
788 .enc
789 .unpack(&mut raw)
790 .map_err(|e| InvocationError::Deserialize(e.to_string()))?;
791 self.pending_acks.push(msg.msg_id);
792 if self.pending_acks.len() >= PENDING_ACKS_THRESHOLD {
793 let ack_body = build_msgs_ack_body(&self.pending_acks);
794 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
795 let _ =
796 Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind).await;
797 self.pending_acks.clear();
798 }
799 if msg.body.len() < 4 {
801 return Ok(msg.body);
802 }
803 let mut need_resend = false;
804 let mut need_session_reset = false;
805 let mut bad_msg_code: Option<u32> = None;
806 let mut bad_msg_server_id: Option<i64> = None;
807 let scan_result = Self::scan_body(
809 &msg.body,
810 &mut self.enc.salt,
811 &mut need_resend,
812 &mut need_session_reset,
813 &mut bad_msg_code,
814 &mut bad_msg_server_id,
815 Some(sent_msg_id),
816 msg.msg_id,
817 )?;
818 if need_session_reset {
819 session_resets += 1;
820 if session_resets > 2 {
821 return Err(InvocationError::Deserialize(
822 "new_session_created (serializable): exceeded 2 resets".into(),
823 ));
824 }
825 if !self.pending_acks.is_empty() {
826 let ack_body = build_msgs_ack_body(&self.pending_acks);
827 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
828 let _ = Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind)
829 .await;
830 self.pending_acks.clear();
831 }
832 if scan_result.is_none() {
833 let (wire, new_id) = self.enc.pack_serializable_with_msg_id(req);
834 sent_msg_id = new_id;
835 Self::send_abridged(&mut self.stream, &wire, &mut self.frame_kind).await?;
836 }
837 } else if need_resend {
838 match bad_msg_code {
839 Some(16) | Some(17) => {
840 if let Some(srv_id) = bad_msg_server_id {
841 self.enc.correct_time_offset(srv_id);
842 }
843 }
845 Some(32) | Some(33) => {
846 self.enc
847 .correct_seq_no(bad_msg_code.expect("matched Some arm"));
848 }
849 _ => {
850 self.enc.undo_seq_no();
851 }
852 }
853 salt_retries += 1;
854 if salt_retries >= 5 {
855 return Err(InvocationError::Deserialize(
856 "bad_server_salt (serializable): exceeded 5 retries".into(),
857 ));
858 }
859 tracing::debug!(
860 "[ferogram::sender] resending serializable request after bad_msg correction (code={bad_msg_code:?}, attempt {salt_retries}/5)"
861 );
862 if !self.pending_acks.is_empty() {
863 let ack_body = build_msgs_ack_body(&self.pending_acks);
864 let (ack_wire, _) = self.enc.pack_body_with_msg_id(&ack_body, false);
865 let _ = Self::send_abridged(&mut self.stream, &ack_wire, &mut self.frame_kind)
866 .await;
867 self.pending_acks.clear();
868 }
869 let (wire, new_id) = self.enc.pack_serializable_with_msg_id(req);
870 sent_msg_id = new_id;
871 Self::send_abridged(&mut self.stream, &wire, &mut self.frame_kind).await?;
872 }
873 if let Some(result) = scan_result {
874 return Ok(result);
875 }
876 }
877 }
878
879 pub async fn rpc_call_raw(&mut self, body: &[u8]) -> Result<Vec<u8>, InvocationError> {
882 Self::send_abridged(&mut self.stream, body, &mut self.frame_kind).await?;
883 Self::recv_abridged(&mut self.stream, &mut self.frame_kind).await
884 }
885
886 async fn send_abridged(
889 stream: &mut TcpStream,
890 data: &[u8],
891 kind: &mut FrameKind,
892 ) -> Result<(), InvocationError> {
893 use tokio::io::AsyncWriteExt as _;
894 match kind {
895 FrameKind::Abridged => {
896 let words = data.len() / 4;
897 let mut frame = if words < 0x7f {
898 let mut v = Vec::with_capacity(1 + data.len());
899 v.push(words as u8);
900 v
901 } else {
902 let mut v = Vec::with_capacity(4 + data.len());
903 v.extend_from_slice(&[
904 0x7f,
905 (words & 0xff) as u8,
906 ((words >> 8) & 0xff) as u8,
907 ((words >> 16) & 0xff) as u8,
908 ]);
909 v
910 };
911 frame.extend_from_slice(data);
912 stream.write_all(&frame).await?;
913 }
914 FrameKind::Intermediate => {
915 let mut frame = Vec::with_capacity(4 + data.len());
916 frame.extend_from_slice(&(data.len() as u32).to_le_bytes());
917 frame.extend_from_slice(data);
918 stream.write_all(&frame).await?;
919 }
920 FrameKind::Full { send_seqno, .. } => {
921 let seq = send_seqno.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
922 let total_len = (data.len() as u32) + 12;
923 let mut packet = Vec::with_capacity(total_len as usize);
924 packet.extend_from_slice(&total_len.to_le_bytes());
925 packet.extend_from_slice(&seq.to_le_bytes());
926 packet.extend_from_slice(data);
927 let crc = ferogram_connect::crc32_ieee(&packet);
928 packet.extend_from_slice(&crc.to_le_bytes());
929 stream.write_all(&packet).await?;
930 }
931 FrameKind::Obfuscated { cipher } => {
932 let words = data.len() / 4;
933 let mut frame = if words < 0x7f {
934 let mut v = Vec::with_capacity(1 + data.len());
935 v.push(words as u8);
936 v
937 } else {
938 let mut v = Vec::with_capacity(4 + data.len());
939 v.extend_from_slice(&[
940 0x7f,
941 (words & 0xff) as u8,
942 ((words >> 8) & 0xff) as u8,
943 ((words >> 16) & 0xff) as u8,
944 ]);
945 v
946 };
947 frame.extend_from_slice(data);
948 cipher.lock().await.encrypt(&mut frame);
949 stream.write_all(&frame).await?;
950 }
951 FrameKind::PaddedIntermediate { cipher } => {
952 let mut pad_len_buf = [0u8; 1];
953 ferogram_crypto::fill_random(&mut pad_len_buf);
954 let pad_len = (pad_len_buf[0] & 0x0f) as usize;
955 let total_payload = data.len() + pad_len;
956 let mut frame = Vec::with_capacity(4 + total_payload);
957 frame.extend_from_slice(&(total_payload as u32).to_le_bytes());
958 frame.extend_from_slice(data);
959 let mut pad = vec![0u8; pad_len];
960 ferogram_crypto::fill_random(&mut pad);
961 frame.extend_from_slice(&pad);
962 cipher.lock().await.encrypt(&mut frame);
963 stream.write_all(&frame).await?;
964 }
965 FrameKind::FakeTls { cipher, .. } => {
966 let mut pad_len_buf = [0u8; 1];
973 ferogram_crypto::fill_random(&mut pad_len_buf);
974 let pad_len = (pad_len_buf[0] & 0x0f) as usize;
975 let total_payload = data.len() + pad_len;
976 let mut frame = Vec::with_capacity(4 + total_payload);
977 frame.extend_from_slice(&(total_payload as u32).to_le_bytes());
978 frame.extend_from_slice(data);
979 let mut pad = vec![0u8; pad_len];
980 ferogram_crypto::fill_random(&mut pad);
981 frame.extend_from_slice(&pad);
982 cipher.lock().await.encrypt(&mut frame);
983
984 let mut wire = Vec::new();
985 ferogram_connect::tls_record::wrap_application_data(&frame, &mut wire);
986 stream.write_all(&wire).await?;
987 }
988 }
989 Ok(())
990 }
991
992 async fn recv_abridged(
994 stream: &mut TcpStream,
995 kind: &mut FrameKind,
996 ) -> Result<Vec<u8>, InvocationError> {
997 use tokio::time::{Duration, timeout};
998 const RECV_TIMEOUT: Duration = Duration::from_secs(60);
999
1000 macro_rules! tread {
1001 ($buf:expr) => {
1002 timeout(RECV_TIMEOUT, stream.read_exact($buf))
1003 .await
1004 .map_err(|_| {
1005 InvocationError::Io(std::io::Error::new(
1006 std::io::ErrorKind::TimedOut,
1007 "transfer recv: timeout (60 s)",
1008 ))
1009 })??
1010 };
1011 }
1012
1013 match kind {
1014 FrameKind::Abridged => {
1015 let mut h = [0u8; 1];
1016 tread!(&mut h);
1017 let words = if h[0] == 0x7f {
1018 let mut b = [0u8; 3];
1019 tread!(&mut b);
1020 let w = b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16;
1021 if w == 1 {
1022 let mut code_buf = [0u8; 4];
1023 tread!(&mut code_buf);
1024 let code = i32::from_le_bytes(code_buf);
1025 return Err(InvocationError::Rpc(
1026 crate::errors::RpcError::from_telegram(code, "transport error"),
1027 ));
1028 }
1029 w
1030 } else {
1031 h[0] as usize
1032 };
1033 let mut buf = vec![0u8; words * 4];
1034 tread!(&mut buf);
1035 if buf.len() == 4 {
1036 let code = i32::from_le_bytes(buf[..4].try_into().unwrap());
1037 if code < 0 {
1038 return Err(InvocationError::Rpc(
1039 crate::errors::RpcError::from_telegram(code, "transport error"),
1040 ));
1041 }
1042 }
1043 Ok(buf)
1044 }
1045 FrameKind::Intermediate => {
1046 let mut len_buf = [0u8; 4];
1047 tread!(&mut len_buf);
1048 let len_i32 = i32::from_le_bytes(len_buf);
1049 if len_i32 < 0 {
1050 return Err(InvocationError::Rpc(
1051 crate::errors::RpcError::from_telegram(len_i32, "transport error"),
1052 ));
1053 }
1054 let mut buf = vec![0u8; len_i32 as usize];
1055 tread!(&mut buf);
1056 Ok(buf)
1057 }
1058 FrameKind::Full { recv_seqno, .. } => {
1059 let mut len_buf = [0u8; 4];
1060 tread!(&mut len_buf);
1061 let total_len_i32 = i32::from_le_bytes(len_buf);
1062 if total_len_i32 < 0 {
1063 return Err(InvocationError::Rpc(
1064 crate::errors::RpcError::from_telegram(total_len_i32, "transport error"),
1065 ));
1066 }
1067 let total_len = total_len_i32 as usize;
1068 if total_len < 12 {
1069 return Err(InvocationError::Deserialize(
1070 "Full transport: packet too short".into(),
1071 ));
1072 }
1073 let mut rest = vec![0u8; total_len - 4];
1074 tread!(&mut rest);
1075 let (body, crc_bytes) = rest.split_at(rest.len() - 4);
1076 let expected_crc = u32::from_le_bytes(crc_bytes.try_into().unwrap());
1077 let mut check_input = Vec::with_capacity(4 + body.len());
1078 check_input.extend_from_slice(&len_buf);
1079 check_input.extend_from_slice(body);
1080 let actual_crc = ferogram_connect::crc32_ieee(&check_input);
1081 if actual_crc != expected_crc {
1082 return Err(InvocationError::Deserialize(format!(
1083 "Full transport: CRC mismatch (got {actual_crc:#010x}, expected {expected_crc:#010x})"
1084 )));
1085 }
1086 let recv_seq = u32::from_le_bytes(body[..4].try_into().unwrap());
1087 let expected_seq = recv_seqno.load(std::sync::atomic::Ordering::Relaxed);
1088 if recv_seq != expected_seq {
1089 return Err(InvocationError::Deserialize(format!(
1090 "Full transport: seqno mismatch (got {recv_seq}, expected {expected_seq})"
1091 )));
1092 }
1093 recv_seqno.store(
1094 expected_seq.wrapping_add(1),
1095 std::sync::atomic::Ordering::Relaxed,
1096 );
1097 Ok(body[4..].to_vec())
1098 }
1099 FrameKind::Obfuscated { cipher } => {
1100 let mut h = [0u8; 1];
1101 tread!(&mut h);
1102 cipher.lock().await.decrypt(&mut h);
1103 let words = if h[0] == 0x7f {
1104 let mut b = [0u8; 3];
1105 tread!(&mut b);
1106 cipher.lock().await.decrypt(&mut b);
1107 let w = b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16;
1108 if w == 1 {
1109 let mut code_buf = [0u8; 4];
1110 tread!(&mut code_buf);
1111 cipher.lock().await.decrypt(&mut code_buf);
1112 let code = i32::from_le_bytes(code_buf);
1113 return Err(InvocationError::Rpc(
1114 crate::errors::RpcError::from_telegram(code, "transport error"),
1115 ));
1116 }
1117 w
1118 } else {
1119 h[0] as usize
1120 };
1121 let mut buf = vec![0u8; words * 4];
1122 tread!(&mut buf);
1123 cipher.lock().await.decrypt(&mut buf);
1124 if buf.len() == 4 {
1125 let code = i32::from_le_bytes(buf[..4].try_into().unwrap());
1126 if code < 0 {
1127 return Err(InvocationError::Rpc(
1128 crate::errors::RpcError::from_telegram(code, "transport error"),
1129 ));
1130 }
1131 }
1132 Ok(buf)
1133 }
1134 FrameKind::PaddedIntermediate { cipher } => {
1135 let mut len_buf = [0u8; 4];
1136 tread!(&mut len_buf);
1137 cipher.lock().await.decrypt(&mut len_buf);
1138 let total_len = i32::from_le_bytes(len_buf);
1139 if total_len < 0 {
1140 return Err(InvocationError::Rpc(
1141 crate::errors::RpcError::from_telegram(total_len, "transport error"),
1142 ));
1143 }
1144 let mut buf = vec![0u8; total_len as usize];
1145 tread!(&mut buf);
1146 cipher.lock().await.decrypt(&mut buf);
1147 if buf.len() >= 24 {
1148 let pad = (buf.len() - 24) % 16;
1149 buf.truncate(buf.len() - pad);
1150 }
1151 Ok(buf)
1152 }
1153 FrameKind::FakeTls {
1154 cipher,
1155 decoded_pending,
1156 ..
1157 } => {
1158 async fn timed(
1159 fut: impl std::future::Future<Output = Result<(), ferogram_connect::ConnectError>>,
1160 ) -> Result<(), InvocationError> {
1161 timeout(RECV_TIMEOUT, fut)
1162 .await
1163 .map_err(|_| {
1164 InvocationError::Io(std::io::Error::new(
1165 std::io::ErrorKind::TimedOut,
1166 "transfer recv: timeout (60 s)",
1167 ))
1168 })?
1169 .map_err(InvocationError::from)
1170 }
1171
1172 let mut len_buf = [0u8; 4];
1173 timed(ferogram_connect::faketls_read_exact(
1174 stream,
1175 cipher,
1176 decoded_pending,
1177 &mut len_buf,
1178 ))
1179 .await?;
1180 let total_len = i32::from_le_bytes(len_buf);
1181 if total_len < 0 {
1182 return Err(InvocationError::Rpc(
1183 crate::errors::RpcError::from_telegram(total_len, "transport error"),
1184 ));
1185 }
1186 let mut buf = vec![0u8; total_len as usize];
1187 timed(ferogram_connect::faketls_read_exact(
1188 stream,
1189 cipher,
1190 decoded_pending,
1191 &mut buf,
1192 ))
1193 .await?;
1194 if buf.len() >= 24 {
1195 let pad = (buf.len() - 24) % 16;
1196 buf.truncate(buf.len() - pad);
1197 }
1198 Ok(buf)
1199 }
1200 }
1201 }
1202
1203 async fn send_plain_frame(
1206 stream: &mut TcpStream,
1207 data: &[u8],
1208 kind: &mut FrameKind,
1209 ) -> Result<(), InvocationError> {
1210 let needs_align = matches!(kind, FrameKind::Abridged | FrameKind::Obfuscated { .. });
1213 if needs_align && !data.len().is_multiple_of(4) {
1214 let mut padded = data.to_vec();
1215 let pad = 4 - (data.len() % 4);
1216 padded.resize(data.len() + pad, 0);
1217 Self::send_abridged(stream, &padded, kind).await
1218 } else {
1219 Self::send_abridged(stream, data, kind).await
1220 }
1221 }
1222
1223 async fn recv_plain_frame<T: Deserializable>(
1224 stream: &mut TcpStream,
1225 kind: &mut FrameKind,
1226 ) -> Result<T, InvocationError> {
1227 let raw = Self::recv_abridged(stream, kind).await?;
1228 if raw.len() == 4 {
1229 let code = i32::from_le_bytes(raw[..4].try_into().unwrap());
1230 if code < 0 {
1231 return Err(InvocationError::Deserialize(format!(
1232 "server transport error during DH: code {code}"
1233 )));
1234 }
1235 }
1236 if raw.len() < 20 {
1237 return Err(InvocationError::Deserialize("plain frame too short".into()));
1238 }
1239 if u64::from_le_bytes(raw[..8].try_into().unwrap()) != 0 {
1240 return Err(InvocationError::Deserialize(
1241 "expected auth_key_id=0 in plaintext".into(),
1242 ));
1243 }
1244 let body_len = u32::from_le_bytes(raw[16..20].try_into().unwrap()) as usize;
1245 if raw.len() < 20 + body_len {
1246 return Err(InvocationError::Deserialize(format!(
1247 "plain frame truncated: have {} bytes, need {}",
1248 raw.len(),
1249 20 + body_len
1250 )));
1251 }
1252 let mut cur = Cursor::from_slice(&raw[20..20 + body_len]);
1253 T::deserialize(&mut cur).map_err(Into::into)
1254 }
1255}