Files
telemt/src/transport/middle_proxy/secret.rs
T

136 lines
4.5 KiB
Rust
Raw Normal View History

2026-02-17 04:16:16 +03:00
use httpdate;
use std::sync::Arc;
2026-03-21 15:45:29 +03:00
use std::time::SystemTime;
use tracing::{debug, info, warn};
2026-02-14 01:36:14 +03:00
use super::http_fetch::https_get;
2026-03-09 20:35:31 +03:00
use super::selftest::record_timeskew_sample;
use crate::transport::UpstreamManager;
2026-03-21 15:45:29 +03:00
use crate::error::{ProxyError, Result};
2026-02-14 01:36:14 +03:00
2026-02-24 18:19:39 +03:00
pub const PROXY_SECRET_MIN_LEN: usize = 32;
pub(super) fn validate_proxy_secret_len(data_len: usize, max_len: usize) -> Result<()> {
if max_len < PROXY_SECRET_MIN_LEN {
return Err(ProxyError::Proxy(format!(
"proxy-secret max length is invalid: {} bytes (must be >= {})",
2026-03-21 15:45:29 +03:00
max_len, PROXY_SECRET_MIN_LEN
2026-02-24 18:19:39 +03:00
)));
}
if data_len < PROXY_SECRET_MIN_LEN {
return Err(ProxyError::Proxy(format!(
"proxy-secret too short: {} bytes (need >= {})",
2026-03-21 15:45:29 +03:00
data_len, PROXY_SECRET_MIN_LEN
2026-02-24 18:19:39 +03:00
)));
}
if data_len > max_len {
return Err(ProxyError::Proxy(format!(
"proxy-secret too long: {} bytes (limit = {})",
2026-03-21 15:45:29 +03:00
data_len, max_len
2026-02-24 18:19:39 +03:00
)));
}
Ok(())
}
2026-02-14 01:36:14 +03:00
/// Fetch Telegram proxy-secret binary.
2026-02-24 18:19:39 +03:00
pub async fn fetch_proxy_secret(cache_path: Option<&str>, max_len: usize) -> Result<Vec<u8>> {
fetch_proxy_secret_with_upstream(cache_path, max_len, None).await
}
/// Fetch Telegram proxy-secret binary, optionally through upstream routing.
pub async fn fetch_proxy_secret_with_upstream(
cache_path: Option<&str>,
max_len: usize,
upstream: Option<Arc<UpstreamManager>>,
) -> Result<Vec<u8>> {
2026-02-14 01:36:14 +03:00
let cache = cache_path.unwrap_or("proxy-secret");
2026-02-14 12:44:20 +03:00
// 1) Try fresh download first.
match download_proxy_secret_with_max_len_via_upstream(max_len, upstream).await {
2026-02-14 12:44:20 +03:00
Ok(data) => {
if let Err(e) = tokio::fs::write(cache, &data).await {
warn!(error = %e, "Failed to cache proxy-secret (non-fatal)");
} else {
debug!(path = cache, len = data.len(), "Cached proxy-secret");
2026-02-14 01:36:14 +03:00
}
2026-02-14 12:44:20 +03:00
return Ok(data);
}
Err(download_err) => {
warn!(error = %download_err, "Proxy-secret download failed, trying cache/file fallback");
// Fall through to cache/file.
2026-02-14 01:36:14 +03:00
}
}
2026-02-24 18:19:39 +03:00
// 2) Fallback to cache/file regardless of age; require len in bounds.
2026-02-14 12:44:20 +03:00
match tokio::fs::read(cache).await {
2026-02-24 18:19:39 +03:00
Ok(data) if validate_proxy_secret_len(data.len(), max_len).is_ok() => {
2026-02-14 12:44:20 +03:00
let age_hours = tokio::fs::metadata(cache)
.await
.ok()
.and_then(|m| m.modified().ok())
.and_then(|m| std::time::SystemTime::now().duration_since(m).ok())
.map(|d| d.as_secs() / 3600);
info!(
path = cache,
len = data.len(),
age_hours,
"Loaded proxy-secret from cache/file after download failure"
);
Ok(data)
}
2026-02-24 18:19:39 +03:00
Ok(data) => validate_proxy_secret_len(data.len(), max_len).map(|_| data),
2026-02-14 12:44:20 +03:00
Err(e) => Err(ProxyError::Proxy(format!(
"Failed to read proxy-secret cache after download failure: {e}"
))),
2026-02-14 01:36:14 +03:00
}
}
2026-02-24 18:19:39 +03:00
pub async fn download_proxy_secret_with_max_len(max_len: usize) -> Result<Vec<u8>> {
download_proxy_secret_with_max_len_via_upstream(max_len, None).await
}
pub async fn download_proxy_secret_with_max_len_via_upstream(
max_len: usize,
upstream: Option<Arc<UpstreamManager>>,
) -> Result<Vec<u8>> {
let resp = https_get("https://core.telegram.org/getProxySecret", upstream).await?;
2026-02-14 01:36:14 +03:00
if !(200..=299).contains(&resp.status) {
2026-02-14 01:36:14 +03:00
return Err(ProxyError::Proxy(format!(
"proxy-secret download HTTP {}",
resp.status
2026-02-14 01:36:14 +03:00
)));
}
if let Some(date_str) = resp.date_header.as_deref()
2026-02-24 05:57:53 +03:00
&& let Ok(server_time) = httpdate::parse_http_date(date_str)
2026-03-21 15:45:29 +03:00
&& let Ok(skew) = SystemTime::now()
.duration_since(server_time)
.or_else(|e| server_time.duration_since(SystemTime::now()).map_err(|_| e))
2026-02-24 05:57:53 +03:00
{
let skew_secs = skew.as_secs();
2026-03-09 20:35:31 +03:00
record_timeskew_sample("proxy_secret_date_header", skew_secs);
2026-02-24 05:57:53 +03:00
if skew_secs > 60 {
2026-03-21 15:45:29 +03:00
warn!(
skew_secs,
"Time skew >60s detected from proxy-secret Date header"
);
2026-02-24 05:57:53 +03:00
} else if skew_secs > 30 {
2026-03-21 15:45:29 +03:00
warn!(
skew_secs,
"Time skew >30s detected from proxy-secret Date header"
);
2026-02-17 04:16:16 +03:00
}
}
let data = resp.body;
2026-02-14 01:36:14 +03:00
2026-02-24 18:19:39 +03:00
validate_proxy_secret_len(data.len(), max_len)?;
2026-02-14 01:36:14 +03:00
info!(len = data.len(), "Downloaded proxy-secret OK");
Ok(data)
}