1use std::sync::Arc;
16use std::time::Duration;
17
18use socket2::TcpKeepalive;
19use tokio::io::AsyncWriteExt;
20use tokio::net::TcpStream;
21
22use ferogram_mtproto::{EncryptedSession, Session, authentication as auth};
23use ferogram_tl_types as tl;
24
25use crate::error::ConnectError;
26use crate::frame::{recv_frame_plain, send_frame};
27use crate::pfs::decode_bind_response;
28use crate::transport::recv_raw_frame;
29use crate::transport_kind::TransportKind;
30
31pub const PING_DELAY_SECS: u64 = 60;
32pub const NO_PING_DISCONNECT: i32 = 75;
33
34const TCP_KEEPALIVE_IDLE_SECS: u64 = 10;
35const TCP_KEEPALIVE_INTERVAL_SECS: u64 = 5;
36#[cfg(not(target_os = "windows"))]
37const TCP_KEEPALIVE_PROBES: u32 = 3;
38
39#[derive(Clone)]
47pub enum FrameKind {
48 Abridged,
49 Intermediate,
50 Full {
51 send_seqno: Arc<std::sync::atomic::AtomicU32>,
52 recv_seqno: Arc<std::sync::atomic::AtomicU32>,
53 },
54 Obfuscated {
56 cipher: std::sync::Arc<tokio::sync::Mutex<ferogram_crypto::ObfuscatedCipher>>,
57 },
58 PaddedIntermediate {
60 cipher: std::sync::Arc<tokio::sync::Mutex<ferogram_crypto::ObfuscatedCipher>>,
61 },
62 FakeTls {
68 cipher: std::sync::Arc<tokio::sync::Mutex<ferogram_crypto::ObfuscatedCipher>>,
69 tls_raw_pending: std::sync::Arc<tokio::sync::Mutex<Vec<u8>>>,
72 decoded_pending: std::sync::Arc<tokio::sync::Mutex<Vec<u8>>>,
75 },
76}
77
78#[derive(Clone, Debug)]
81pub struct FutureSalt {
82 pub valid_since: i32,
83 pub valid_until: u32,
88 pub salt: i64,
89}
90
91pub const SALT_USE_DELAY: i32 = 60;
94
95pub struct Connection {
96 pub stream: TcpStream,
97 pub enc: EncryptedSession,
98 pub frame_kind: FrameKind,
99 pub perm_auth_key: Option<[u8; 256]>,
103}
104
105impl Connection {
106 async fn open_stream(
108 addr: &str,
109 socks5: Option<&crate::socks5::Socks5Config>,
110 transport: &TransportKind,
111 dc_id: i16,
112 ) -> Result<(TcpStream, FrameKind), ConnectError> {
113 let stream = match socks5 {
114 Some(proxy) => proxy.connect(addr).await?,
115 None => {
116 let stream = TcpStream::connect(addr).await.map_err(ConnectError::Io)?;
117 stream.set_nodelay(true).ok();
118 {
119 let sock = socket2::SockRef::from(&stream);
120 let keepalive = TcpKeepalive::new()
121 .with_time(Duration::from_secs(TCP_KEEPALIVE_IDLE_SECS))
122 .with_interval(Duration::from_secs(TCP_KEEPALIVE_INTERVAL_SECS));
123 #[cfg(not(target_os = "windows"))]
124 let keepalive = keepalive.with_retries(TCP_KEEPALIVE_PROBES);
125 sock.set_tcp_keepalive(&keepalive).ok();
126 }
127 stream
128 }
129 };
130 Self::apply_transport_init(stream, transport, dc_id).await
131 }
132
133 async fn open_stream_mtproxy(
136 mtproxy: &crate::proxy::MtProxyConfig,
137 dc_id: i16,
138 ) -> Result<(TcpStream, FrameKind), ConnectError> {
139 let stream = mtproxy.connect().await?;
140 stream.set_nodelay(true).ok();
141 Self::apply_transport_init(stream, &mtproxy.transport, dc_id).await
142 }
143
144 async fn apply_transport_init(
145 mut stream: TcpStream,
146 transport: &TransportKind,
147 dc_id: i16,
148 ) -> Result<(TcpStream, FrameKind), ConnectError> {
149 match transport {
150 TransportKind::Abridged => {
151 stream.write_all(&[0xef]).await?;
152 Ok((stream, FrameKind::Abridged))
153 }
154 TransportKind::Intermediate => {
155 stream.write_all(&[0xee, 0xee, 0xee, 0xee]).await?;
156 Ok((stream, FrameKind::Intermediate))
157 }
158 TransportKind::Full => {
159 Ok((
161 stream,
162 FrameKind::Full {
163 send_seqno: Arc::new(std::sync::atomic::AtomicU32::new(0)),
164 recv_seqno: Arc::new(std::sync::atomic::AtomicU32::new(0)),
165 },
166 ))
167 }
168 TransportKind::Obfuscated { secret } => {
169 let proxy_secret = secret.as_ref().map(|s| s.as_ref());
170 let (nonce, cipher) =
171 ferogram_crypto::build_obfuscated_init(0xef, dc_id, proxy_secret);
172 stream.write_all(&nonce).await?;
173 let cipher_arc = std::sync::Arc::new(tokio::sync::Mutex::new(cipher));
174 Ok((stream, FrameKind::Obfuscated { cipher: cipher_arc }))
175 }
176 TransportKind::PaddedIntermediate { secret } => {
177 let proxy_secret = secret.as_ref().map(|s| s.as_ref());
178 let (nonce, cipher) =
179 ferogram_crypto::build_obfuscated_init(0xdd, dc_id, proxy_secret);
180 stream.write_all(&nonce).await?;
181 let cipher_arc = std::sync::Arc::new(tokio::sync::Mutex::new(cipher));
182 Ok((stream, FrameKind::PaddedIntermediate { cipher: cipher_arc }))
183 }
184 TransportKind::FakeTls { secret, domain } => {
185 let domain_bytes = domain.as_bytes();
193 let mut session_id = [0u8; 32];
194 ferogram_crypto::fill_random(&mut session_id);
195
196 let mut grease_seed = [0u8; 4];
204 ferogram_crypto::fill_random(&mut grease_seed);
205 let grease_u16 = |b: u8| -> u16 {
206 let v = (b & 0xf0) | 0x0a;
207 ((v as u16) << 8) | v as u16
208 };
209 let grease_cipher = grease_u16(grease_seed[0]);
210 let grease_group = grease_u16(grease_seed[1]);
211 let grease_version = grease_u16(grease_seed[2]);
212 let grease_ext = grease_u16(grease_seed[3]);
213
214 let mut cipher_suites = Vec::new();
216 cipher_suites.extend_from_slice(&grease_cipher.to_be_bytes());
217 for c in [
218 0x1301u16, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030, 0xcca9, 0xcca8,
219 0xc013, 0xc014, 0x009c, 0x009d, 0x002f, 0x0035,
220 ] {
221 cipher_suites.extend_from_slice(&c.to_be_bytes());
222 }
223 let compression: &[u8] = &[0x01, 0x00];
224
225 let sni_name_len = domain_bytes.len() as u16;
226 let sni_list_len = sni_name_len + 3;
227 let sni_ext_len = sni_list_len + 2;
228 let mut sni_ext = Vec::new();
229 sni_ext.extend_from_slice(&[0x00, 0x00]);
230 sni_ext.extend_from_slice(&sni_ext_len.to_be_bytes());
231 sni_ext.extend_from_slice(&sni_list_len.to_be_bytes());
232 sni_ext.push(0x00);
233 sni_ext.extend_from_slice(&sni_name_len.to_be_bytes());
234 sni_ext.extend_from_slice(domain_bytes);
235
236 let sup_grp: [u8; 12] = [
237 0x00,
238 0x0a,
239 0x00,
240 0x08,
241 0x00,
242 0x06,
243 (grease_group >> 8) as u8,
244 grease_group as u8,
245 0x00,
246 0x1d,
247 0x00,
248 0x17,
249 ];
250 let ec_point_fmt: &[u8] = &[0x00, 0x0b, 0x00, 0x02, 0x01, 0x00];
251 let sig_algs: &[u8] = &[
252 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05,
253 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01,
254 ];
255 let alpn: &[u8] = &[
256 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, b'h', b'2', 0x08, b'h', b't', b't',
257 b'p', b'/', b'1', b'.', b'1',
258 ];
259 let ems: &[u8] = &[0x00, 0x17, 0x00, 0x00];
260 let sess_tick: &[u8] = &[0x00, 0x23, 0x00, 0x00];
261 let sup_ver: Vec<u8> = {
262 let mut v = vec![0x00, 0x2b, 0x00, 0x05, 0x04];
263 v.extend_from_slice(&grease_version.to_be_bytes());
264 v.extend_from_slice(&[0x03, 0x04]);
265 v
266 };
267 let psk_modes: &[u8] = &[0x00, 0x2d, 0x00, 0x02, 0x01, 0x01];
268 let mut key_share_pub = [0u8; 32];
269 ferogram_crypto::fill_random(&mut key_share_pub);
270 let key_share: Vec<u8> = {
271 let mut v = vec![0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20];
272 v.extend_from_slice(&key_share_pub);
273 v
274 };
275 let reneg_info: &[u8] = &[0xff, 0x01, 0x00, 0x01, 0x00];
276 let grease_ext_entry: [u8; 6] = [
277 (grease_ext >> 8) as u8,
278 grease_ext as u8,
279 0x00,
280 0x02,
281 0x00,
282 0x00,
283 ];
284
285 let mut ext_body = Vec::new();
286 ext_body.extend_from_slice(&sni_ext);
287 ext_body.extend_from_slice(&sup_grp);
288 ext_body.extend_from_slice(ec_point_fmt);
289 ext_body.extend_from_slice(sig_algs);
290 ext_body.extend_from_slice(alpn);
291 ext_body.extend_from_slice(ems);
292 ext_body.extend_from_slice(sess_tick);
293 ext_body.extend_from_slice(&sup_ver);
294 ext_body.extend_from_slice(psk_modes);
295 ext_body.extend_from_slice(&key_share);
296 ext_body.extend_from_slice(reneg_info);
297 ext_body.extend_from_slice(&grease_ext_entry);
298
299 const TARGET_HELLO_LEN: usize = 517;
303 let prefix_len = 2 + 32 + 1 + session_id.len() + 2 + cipher_suites.len()
305 + compression.len() + 2 ;
306 let unpadded_total = prefix_len + ext_body.len();
307 if unpadded_total < TARGET_HELLO_LEN {
308 let pad_needed = TARGET_HELLO_LEN - unpadded_total - 4; ext_body.extend_from_slice(&[0x00, 0x15]);
310 ext_body.extend_from_slice(&(pad_needed as u16).to_be_bytes());
311 ext_body.extend(std::iter::repeat_n(0u8, pad_needed));
312 }
313
314 let mut extensions = Vec::new();
315 extensions.extend_from_slice(&(ext_body.len() as u16).to_be_bytes());
316 extensions.extend_from_slice(&ext_body);
317
318 let mut hello_body = Vec::new();
319 hello_body.extend_from_slice(&[0x03, 0x03]);
320 hello_body.extend_from_slice(&[0u8; 32]); hello_body.push(session_id.len() as u8);
322 hello_body.extend_from_slice(&session_id);
323 hello_body.extend_from_slice(&(cipher_suites.len() as u16).to_be_bytes());
324 hello_body.extend_from_slice(&cipher_suites);
325 hello_body.extend_from_slice(compression);
326 hello_body.extend_from_slice(&extensions);
327
328 let hs_len = hello_body.len() as u32;
329 let mut handshake = vec![
330 0x01,
331 ((hs_len >> 16) & 0xff) as u8,
332 ((hs_len >> 8) & 0xff) as u8,
333 (hs_len & 0xff) as u8,
334 ];
335 handshake.extend_from_slice(&hello_body);
336
337 let rec_len = handshake.len() as u16;
338 let mut record = Vec::new();
339 record.push(0x16);
340 record.extend_from_slice(&[0x03, 0x01]);
341 record.extend_from_slice(&rec_len.to_be_bytes());
342 record.extend_from_slice(&handshake);
343
344 const CLIENT_RANDOM_OFFSET: usize = 5 + 4 + 2;
346 let client_digest = ferogram_crypto::fake_tls_client_digest(secret, &record);
349 record[CLIENT_RANDOM_OFFSET..CLIENT_RANDOM_OFFSET + 32]
350 .copy_from_slice(&client_digest);
351 stream.write_all(&record).await?;
352
353 let hello_rec = crate::tls_record::read_one_record(&mut stream)
359 .await
360 .map_err(ConnectError::Io)?;
361 if hello_rec.rec_type != crate::tls_record::RECORD_HANDSHAKE {
362 return Err(ConnectError::Other(format!(
363 "FakeTLS: expected ServerHello (Handshake) record, got type 0x{:02x}",
364 hello_rec.rec_type
365 )));
366 }
367 let ccs_rec = crate::tls_record::read_one_record(&mut stream)
368 .await
369 .map_err(ConnectError::Io)?;
370 if ccs_rec.rec_type != crate::tls_record::RECORD_CHANGE_CIPHER_SPEC {
371 return Err(ConnectError::Other(format!(
372 "FakeTLS: expected ChangeCipherSpec record, got type 0x{:02x}",
373 ccs_rec.rec_type
374 )));
375 }
376 let app_rec = crate::tls_record::read_one_record(&mut stream)
377 .await
378 .map_err(ConnectError::Io)?;
379 if app_rec.rec_type != crate::tls_record::RECORD_APPLICATION_DATA {
380 return Err(ConnectError::Other(format!(
381 "FakeTLS: expected Application Data record, got type 0x{:02x}",
382 app_rec.rec_type
383 )));
384 }
385
386 let mut packet = Vec::with_capacity(
387 hello_rec.bytes.len() + ccs_rec.bytes.len() + app_rec.bytes.len(),
388 );
389 packet.extend_from_slice(&hello_rec.bytes);
390 packet.extend_from_slice(&ccs_rec.bytes);
391 packet.extend_from_slice(&app_rec.bytes);
392
393 const SERVER_DIGEST_OFFSET: usize = 11;
396 if packet.len() < SERVER_DIGEST_OFFSET + 32 {
397 return Err(ConnectError::Other("FakeTLS: ServerHello too short".into()));
398 }
399 let mut server_digest = [0u8; 32];
400 server_digest
401 .copy_from_slice(&packet[SERVER_DIGEST_OFFSET..SERVER_DIGEST_OFFSET + 32]);
402 packet[SERVER_DIGEST_OFFSET..SERVER_DIGEST_OFFSET + 32].fill(0);
403
404 if !ferogram_crypto::fake_tls_verify_server_digest(
405 secret,
406 &client_digest,
407 &packet,
408 &server_digest,
409 ) {
410 return Err(ConnectError::Other(
411 "FakeTLS: server digest verification failed (wrong secret, wrong \
412 domain, or the proxy does not speak FakeTLS)"
413 .into(),
414 ));
415 }
416
417 let (nonce, cipher) =
423 ferogram_crypto::build_obfuscated_init(0xdd, dc_id, Some(secret.as_ref()));
424
425 let mut first_write = Vec::new();
426 first_write.extend_from_slice(&crate::tls_record::change_cipher_spec_record());
427 crate::tls_record::wrap_application_data(&nonce, &mut first_write);
428 stream.write_all(&first_write).await?;
429
430 let cipher_arc = std::sync::Arc::new(tokio::sync::Mutex::new(cipher));
431 Ok((
432 stream,
433 FrameKind::FakeTls {
434 cipher: cipher_arc,
435 tls_raw_pending: std::sync::Arc::new(tokio::sync::Mutex::new(Vec::new())),
436 decoded_pending: std::sync::Arc::new(tokio::sync::Mutex::new(Vec::new())),
437 },
438 ))
439 }
440 TransportKind::Http => {
441 stream.write_all(&[0xef]).await?;
443 Ok((stream, FrameKind::Abridged))
444 }
445 }
446 }
447
448 pub async fn open_stream_pub(
453 addr: &str,
454 dc_id: i16,
455 transport: &TransportKind,
456 socks5: Option<&crate::socks5::Socks5Config>,
457 mtproxy: Option<&crate::proxy::MtProxyConfig>,
458 ) -> Result<(TcpStream, FrameKind), ConnectError> {
459 if let Some(mp) = mtproxy {
460 Self::open_stream_mtproxy(mp, dc_id).await
461 } else {
462 Self::open_stream(addr, socks5, transport, dc_id).await
463 }
464 }
465
466 pub async fn connect_raw(
471 addr: &str,
472 socks5: Option<&crate::socks5::Socks5Config>,
473 mtproxy: Option<&crate::proxy::MtProxyConfig>,
474 transport: &TransportKind,
475 dc_id: i16,
476 ) -> Result<Self, ConnectError> {
477 let t_label = match transport {
478 TransportKind::Abridged => "Abridged",
479 TransportKind::Obfuscated { .. } => "Obfuscated",
480 TransportKind::PaddedIntermediate { .. } => "PaddedIntermediate",
481 TransportKind::Http => "Http",
482 TransportKind::Intermediate => "Intermediate",
483 TransportKind::Full => "Full",
484 TransportKind::FakeTls { .. } => "FakeTls",
485 };
486 tracing::debug!("[ferogram::connect] starting DH handshake with {addr} via {t_label}");
487
488 let addr2 = addr.to_string();
489 let socks5_c = socks5.cloned();
490 let mtproxy_c = mtproxy.cloned();
491 let transport_c = transport.clone();
492
493 let fut = async move {
494 let (mut stream, frame_kind) = if let Some(ref mp) = mtproxy_c {
495 Self::open_stream_mtproxy(mp, dc_id).await?
496 } else {
497 Self::open_stream(&addr2, socks5_c.as_ref(), &transport_c, dc_id).await?
498 };
499
500 let mut plain = Session::new();
501
502 let (req1, s1) = auth::step1().map_err(|e| ConnectError::other(e.to_string()))?;
503 send_frame(
504 &mut stream,
505 &plain.pack(&req1).to_plaintext_bytes(),
506 &frame_kind,
507 )
508 .await?;
509 let res_pq: tl::enums::ResPq = recv_frame_plain(&mut stream, &frame_kind).await?;
510
511 let (req2, s2) = auth::step2(s1, res_pq, dc_id as i32)
512 .map_err(|e| ConnectError::other(e.to_string()))?;
513 send_frame(
514 &mut stream,
515 &plain.pack(&req2).to_plaintext_bytes(),
516 &frame_kind,
517 )
518 .await?;
519 let dh: tl::enums::ServerDhParams = recv_frame_plain(&mut stream, &frame_kind).await?;
520
521 let (req3, s3) = auth::step3(s2, dh).map_err(|e| ConnectError::other(e.to_string()))?;
522 send_frame(
523 &mut stream,
524 &plain.pack(&req3).to_plaintext_bytes(),
525 &frame_kind,
526 )
527 .await?;
528 let ans: tl::enums::SetClientDhParamsAnswer =
529 recv_frame_plain(&mut stream, &frame_kind).await?;
530
531 let done = {
533 let mut result =
534 auth::finish(s3, ans).map_err(|e| ConnectError::other(e.to_string()))?;
535 let mut attempts = 0u8;
536 loop {
537 match result {
538 auth::FinishResult::Done(d) => break d,
539 auth::FinishResult::Retry {
540 retry_id,
541 dh_params,
542 nonce,
543 server_nonce,
544 new_nonce,
545 } => {
546 attempts += 1;
547 if attempts >= 5 {
548 return Err(ConnectError::other(
549 "dh_gen_retry exceeded 5 attempts",
550 ));
551 }
552 let (req_retry, s3_retry) = auth::retry_step3(
553 &dh_params,
554 nonce,
555 server_nonce,
556 new_nonce,
557 retry_id,
558 )
559 .map_err(|e| ConnectError::other(e.to_string()))?;
560 send_frame(
561 &mut stream,
562 &plain.pack(&req_retry).to_plaintext_bytes(),
563 &frame_kind,
564 )
565 .await?;
566 let ans_retry: tl::enums::SetClientDhParamsAnswer =
567 recv_frame_plain(&mut stream, &frame_kind).await?;
568 result = auth::finish(s3_retry, ans_retry)
569 .map_err(|e| ConnectError::other(e.to_string()))?;
570 }
571 }
572 }
573 };
574 tracing::debug!("[ferogram::connect] DH handshake complete, auth key established");
575
576 Ok::<Self, ConnectError>(Self {
577 stream,
578 enc: EncryptedSession::new(done.auth_key, done.first_salt, done.time_offset),
579 frame_kind,
580 perm_auth_key: None, })
582 };
583
584 tokio::time::timeout(Duration::from_secs(15), fut)
585 .await
586 .map_err(|_| {
587 ConnectError::other(format!("DH handshake with {addr} timed out after 15 s"))
588 })?
589 }
590
591 #[allow(clippy::too_many_arguments)]
596 pub async fn connect_with_key(
597 addr: &str,
598 auth_key: [u8; 256],
599 first_salt: i64,
600 time_offset: i32,
601 socks5: Option<&crate::socks5::Socks5Config>,
602 mtproxy: Option<&crate::proxy::MtProxyConfig>,
603 transport: &TransportKind,
604 dc_id: i16,
605 pfs: bool,
606 ) -> Result<Self, ConnectError> {
607 let addr2 = addr.to_string();
608 let socks5_c = socks5.cloned();
609 let mtproxy_c = mtproxy.cloned();
610 let transport_c = transport.clone();
611
612 let fut = async move {
613 let (mut stream, frame_kind) = if let Some(ref mp) = mtproxy_c {
614 Self::open_stream_mtproxy(mp, dc_id).await?
615 } else {
616 Self::open_stream(&addr2, socks5_c.as_ref(), &transport_c, dc_id).await?
617 };
618 if pfs {
619 tracing::debug!("[ferogram::connect] PFS: binding temporary key for DC{dc_id}");
620 match Self::do_pfs_bind(&mut stream, &frame_kind, &auth_key, dc_id).await {
621 Ok(temp_enc) => {
622 tracing::debug!(
623 "[ferogram::connect] PFS: temporary key bound for DC{dc_id}"
624 );
625 return Ok(Self {
626 stream,
627 enc: temp_enc,
628 frame_kind,
629 perm_auth_key: Some(auth_key),
630 });
631 }
632 Err(e) => {
633 tracing::warn!(
634 "[ferogram::connect] PFS bind failed for DC{dc_id} ({e}); falling back to permanent key"
635 );
636 return Err(e);
639 }
640 }
641 }
642 Ok::<Self, ConnectError>(Self {
643 stream,
644 enc: EncryptedSession::new(auth_key, first_salt, time_offset),
645 frame_kind,
646 perm_auth_key: None,
647 })
648 };
649
650 tokio::time::timeout(Duration::from_secs(30), fut)
651 .await
652 .map_err(|_| {
653 ConnectError::Io(std::io::Error::new(
654 std::io::ErrorKind::TimedOut,
655 format!("connect_with_key to {addr} timed out after 30 s"),
656 ))
657 })?
658 }
659
660 async fn do_pfs_bind(
664 stream: &mut TcpStream,
665 frame_kind: &FrameKind,
666 perm_auth_key: &[u8; 256],
667 dc_id: i16,
668 ) -> Result<EncryptedSession, ConnectError> {
669 use ferogram_mtproto::{
670 auth_key_id_from_key, encrypt_bind_inner, gen_msg_id, new_seen_msg_ids,
671 serialize_bind_temp_auth_key,
672 };
673 const TEMP_EXPIRES: i32 = 86_400; let mut plain = Session::new();
677
678 let (req1, s1) = auth::step1().map_err(|e| ConnectError::other(e.to_string()))?;
679 send_frame(stream, &plain.pack(&req1).to_plaintext_bytes(), frame_kind).await?;
680 let res_pq: tl::enums::ResPq = recv_frame_plain(stream, frame_kind).await?;
681
682 let (req2, s2) = ferogram_mtproto::step2_temp(s1, res_pq, dc_id as i32, TEMP_EXPIRES)
683 .map_err(|e| ConnectError::other(e.to_string()))?;
684 send_frame(stream, &plain.pack(&req2).to_plaintext_bytes(), frame_kind).await?;
685 let dh: tl::enums::ServerDhParams = recv_frame_plain(stream, frame_kind).await?;
686
687 let (req3, s3) = auth::step3(s2, dh).map_err(|e| ConnectError::other(e.to_string()))?;
688 send_frame(stream, &plain.pack(&req3).to_plaintext_bytes(), frame_kind).await?;
689 let ans: tl::enums::SetClientDhParamsAnswer = recv_frame_plain(stream, frame_kind).await?;
690
691 let done = {
692 let mut result =
693 auth::finish(s3, ans).map_err(|e| ConnectError::other(e.to_string()))?;
694 let mut attempts = 0u8;
695 loop {
696 match result {
697 ferogram_mtproto::FinishResult::Done(d) => break d,
698 ferogram_mtproto::FinishResult::Retry {
699 retry_id,
700 dh_params,
701 nonce,
702 server_nonce,
703 new_nonce,
704 } => {
705 attempts += 1;
706 if attempts >= 5 {
707 return Err(ConnectError::other(
708 "PFS temp DH retry exceeded 5 attempts",
709 ));
710 }
711 let (rr, s3r) = ferogram_mtproto::retry_step3(
712 &dh_params,
713 nonce,
714 server_nonce,
715 new_nonce,
716 retry_id,
717 )
718 .map_err(|e| ConnectError::other(e.to_string()))?;
719 send_frame(stream, &plain.pack(&rr).to_plaintext_bytes(), frame_kind)
720 .await?;
721 let ar: tl::enums::SetClientDhParamsAnswer =
722 recv_frame_plain(stream, frame_kind).await?;
723 result = auth::finish(s3r, ar)
724 .map_err(|e| ConnectError::other(e.to_string()))?;
725 }
726 }
727 }
728 };
729
730 let temp_key = done.auth_key;
731 let temp_salt = done.first_salt;
732 let temp_offset = done.time_offset;
733
734 let temp_key_id = auth_key_id_from_key(&temp_key);
736 let perm_key_id = auth_key_id_from_key(perm_auth_key);
737
738 let mut nonce_buf = [0u8; 8];
739 ferogram_crypto::fill_random(&mut nonce_buf);
740 let nonce = i64::from_le_bytes(nonce_buf);
741
742 let server_now = std::time::SystemTime::now()
743 .duration_since(std::time::UNIX_EPOCH)
744 .unwrap()
745 .as_secs() as i32
746 + temp_offset;
747 let expires_at = server_now + TEMP_EXPIRES;
748
749 let seen = new_seen_msg_ids();
750 let mut temp_enc = EncryptedSession::with_seen(temp_key, temp_salt, temp_offset, seen);
751 let temp_session_id = temp_enc.session_id();
752
753 let msg_id = gen_msg_id();
754 let enc_msg = encrypt_bind_inner(
755 perm_auth_key,
756 msg_id,
757 nonce,
758 temp_key_id,
759 perm_key_id,
760 temp_session_id,
761 expires_at,
762 );
763 let bind_body = serialize_bind_temp_auth_key(perm_key_id, nonce, expires_at, &enc_msg);
764
765 let wire = temp_enc.pack_body_at_msg_id(&bind_body, msg_id);
767 send_frame(stream, &wire, frame_kind).await?;
768
769 for attempt in 0u8..5 {
773 let mut raw = recv_raw_frame(stream, frame_kind).await?;
774 let decrypted = temp_enc
775 .unpack(&mut raw)
776 .map_err(|e| ConnectError::other(format!("PFS bind decrypt: {e:?}")))?;
777 match decode_bind_response(&decrypted.body) {
778 Ok(()) => {
779 return Ok(temp_enc);
783 }
784 Err(ref e) if e == "__need_more__" => {
785 tracing::debug!(
786 "[ferogram::connect] PFS (DC{dc_id}): got informational frame on attempt {attempt}, reading next"
787 );
788 continue;
789 }
790 Err(reason) => {
791 tracing::error!(
792 "[ferogram::connect] PFS bind rejected by server for DC{dc_id}: {reason}"
793 );
794 return Err(ConnectError::other(format!(
795 "auth.bindTempAuthKey: {reason}"
796 )));
797 }
798 }
799 }
800 Err(ConnectError::other(
801 "auth.bindTempAuthKey: no boolTrue after 5 frames",
802 ))
803 }
804
805 pub fn auth_key_bytes(&self) -> [u8; 256] {
809 self.perm_auth_key
812 .unwrap_or_else(|| self.enc.auth_key_bytes())
813 }
814
815 pub async fn connect_to_dc(
821 addr: &str,
822 dc_id: i16,
823 transport: &TransportKind,
824 socks5: Option<&crate::socks5::Socks5Config>,
825 mtproxy: Option<&crate::proxy::MtProxyConfig>,
826 ) -> Result<(TcpStream, FrameKind, EncryptedSession), ConnectError> {
827 let conn = Self::connect_raw(addr, socks5, mtproxy, transport, dc_id).await?;
828 Ok((conn.stream, conn.frame_kind, conn.enc))
829 }
830}
831
832pub async fn connect_to_dc(
837 addr: &str,
838 dc_id: i16,
839 transport: &TransportKind,
840 socks5: Option<&crate::socks5::Socks5Config>,
841 mtproxy: Option<&crate::proxy::MtProxyConfig>,
842) -> Result<(TcpStream, FrameKind, EncryptedSession), ConnectError> {
843 Connection::connect_to_dc(addr, dc_id, transport, socks5, mtproxy).await
844}