mirror of
https://github.com/telemt/telemt.git
synced 2026-07-25 15:06:15 +03:00
Compare commits
10 Commits
3.3.37
...
24eb507e91
| Author | SHA1 | Date | |
|---|---|---|---|
| 24eb507e91 | |||
| 977ee53b72 | |||
| 8fe6fcb7eb | |||
| 9b64d2ee17 | |||
| 873618ce53 | |||
| f6704d7d65 | |||
| 3d20002e56 | |||
| 8fcd0fa950 | |||
| 645e968778 | |||
| b46216d357 |
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
***Löst Probleme, bevor andere überhaupt wissen, dass sie existieren*** / ***It solves problems before others even realize they exist***
|
***Löst Probleme, bevor andere überhaupt wissen, dass sie existieren*** / ***It solves problems before others even realize they exist***
|
||||||
|
|
||||||
[**Telemt Chat in Telegram**](https://t.me/telemtrs)
|
### [**Telemt Chat in Telegram**](https://t.me/telemtrs)
|
||||||
|
|
||||||
**Telemt** is a fast, secure, and feature-rich server written in Rust: it fully implements the official Telegram proxy algo and adds many production-ready improvements such as:
|
**Telemt** is a fast, secure, and feature-rich server written in Rust: it fully implements the official Telegram proxy algo and adds many production-ready improvements such as:
|
||||||
- [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + Generation Lifecycle](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md)
|
- [ME Pool + Reader/Writer + Registry + Refill + Adaptive Floor + Trio-State + Generation Lifecycle](https://github.com/telemt/telemt/blob/main/docs/model/MODEL.en.md)
|
||||||
|
|||||||
+3056
-315
File diff suppressed because it is too large
Load Diff
@@ -100,7 +100,7 @@ pub(crate) fn default_fake_cert_len() -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_tls_front_dir() -> String {
|
pub(crate) fn default_tls_front_dir() -> String {
|
||||||
"tlsfront".to_string()
|
"/etc/telemt/tlsfront".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_replay_check_len() -> usize {
|
pub(crate) fn default_replay_check_len() -> usize {
|
||||||
@@ -302,7 +302,7 @@ pub(crate) fn default_me2dc_fallback() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_me2dc_fast() -> bool {
|
pub(crate) fn default_me2dc_fast() -> bool {
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_keepalive_interval() -> u64 {
|
pub(crate) fn default_keepalive_interval() -> u64 {
|
||||||
@@ -558,7 +558,7 @@ pub(crate) fn default_beobachten_flush_secs() -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_beobachten_file() -> String {
|
pub(crate) fn default_beobachten_file() -> String {
|
||||||
"cache/beobachten.txt".to_string()
|
"/etc/telemt/beobachten.txt".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_tls_new_session_tickets() -> u8 {
|
pub(crate) fn default_tls_new_session_tickets() -> u8 {
|
||||||
|
|||||||
+82
-15
@@ -18,19 +18,38 @@ use crate::transport::middle_proxy::{
|
|||||||
pub(crate) fn resolve_runtime_config_path(
|
pub(crate) fn resolve_runtime_config_path(
|
||||||
config_path_cli: &str,
|
config_path_cli: &str,
|
||||||
startup_cwd: &std::path::Path,
|
startup_cwd: &std::path::Path,
|
||||||
|
config_path_explicit: bool,
|
||||||
) -> PathBuf {
|
) -> PathBuf {
|
||||||
let raw = PathBuf::from(config_path_cli);
|
if config_path_explicit {
|
||||||
let absolute = if raw.is_absolute() {
|
let raw = PathBuf::from(config_path_cli);
|
||||||
raw
|
let absolute = if raw.is_absolute() {
|
||||||
} else {
|
raw
|
||||||
startup_cwd.join(raw)
|
} else {
|
||||||
};
|
startup_cwd.join(raw)
|
||||||
absolute.canonicalize().unwrap_or(absolute)
|
};
|
||||||
|
return absolute.canonicalize().unwrap_or(absolute);
|
||||||
|
}
|
||||||
|
|
||||||
|
let etc_telemt = std::path::Path::new("/etc/telemt");
|
||||||
|
let candidates = [
|
||||||
|
startup_cwd.join("config.toml"),
|
||||||
|
startup_cwd.join("telemt.toml"),
|
||||||
|
etc_telemt.join("telemt.toml"),
|
||||||
|
etc_telemt.join("config.toml"),
|
||||||
|
];
|
||||||
|
for candidate in candidates {
|
||||||
|
if candidate.is_file() {
|
||||||
|
return candidate.canonicalize().unwrap_or(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startup_cwd.join("config.toml")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsed CLI arguments.
|
/// Parsed CLI arguments.
|
||||||
pub(crate) struct CliArgs {
|
pub(crate) struct CliArgs {
|
||||||
pub config_path: String,
|
pub config_path: String,
|
||||||
|
pub config_path_explicit: bool,
|
||||||
pub data_path: Option<PathBuf>,
|
pub data_path: Option<PathBuf>,
|
||||||
pub silent: bool,
|
pub silent: bool,
|
||||||
pub log_level: Option<String>,
|
pub log_level: Option<String>,
|
||||||
@@ -39,6 +58,7 @@ pub(crate) struct CliArgs {
|
|||||||
|
|
||||||
pub(crate) fn parse_cli() -> CliArgs {
|
pub(crate) fn parse_cli() -> CliArgs {
|
||||||
let mut config_path = "config.toml".to_string();
|
let mut config_path = "config.toml".to_string();
|
||||||
|
let mut config_path_explicit = false;
|
||||||
let mut data_path: Option<PathBuf> = None;
|
let mut data_path: Option<PathBuf> = None;
|
||||||
let mut silent = false;
|
let mut silent = false;
|
||||||
let mut log_level: Option<String> = None;
|
let mut log_level: Option<String> = None;
|
||||||
@@ -74,6 +94,20 @@ pub(crate) fn parse_cli() -> CliArgs {
|
|||||||
s.trim_start_matches("--data-path=").to_string(),
|
s.trim_start_matches("--data-path=").to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
"--working-dir" => {
|
||||||
|
i += 1;
|
||||||
|
if i < args.len() {
|
||||||
|
data_path = Some(PathBuf::from(args[i].clone()));
|
||||||
|
} else {
|
||||||
|
eprintln!("Missing value for --working-dir");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s if s.starts_with("--working-dir=") => {
|
||||||
|
data_path = Some(PathBuf::from(
|
||||||
|
s.trim_start_matches("--working-dir=").to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
"--silent" | "-s" => {
|
"--silent" | "-s" => {
|
||||||
silent = true;
|
silent = true;
|
||||||
}
|
}
|
||||||
@@ -111,13 +145,11 @@ pub(crate) fn parse_cli() -> CliArgs {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s if s.starts_with("--working-dir") => {
|
|
||||||
if !s.contains('=') {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s if !s.starts_with('-') => {
|
s if !s.starts_with('-') => {
|
||||||
config_path = s.to_string();
|
if !matches!(s, "run" | "start" | "stop" | "reload" | "status") {
|
||||||
|
config_path = s.to_string();
|
||||||
|
config_path_explicit = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
eprintln!("Unknown option: {}", other);
|
eprintln!("Unknown option: {}", other);
|
||||||
@@ -128,6 +160,7 @@ pub(crate) fn parse_cli() -> CliArgs {
|
|||||||
|
|
||||||
CliArgs {
|
CliArgs {
|
||||||
config_path,
|
config_path,
|
||||||
|
config_path_explicit,
|
||||||
data_path,
|
data_path,
|
||||||
silent,
|
silent,
|
||||||
log_level,
|
log_level,
|
||||||
@@ -152,6 +185,7 @@ fn print_help() {
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
" --data-path <DIR> Set data directory (absolute path; overrides config value)"
|
" --data-path <DIR> Set data directory (absolute path; overrides config value)"
|
||||||
);
|
);
|
||||||
|
eprintln!(" --working-dir <DIR> Alias for --data-path");
|
||||||
eprintln!(" --silent, -s Suppress info logs");
|
eprintln!(" --silent, -s Suppress info logs");
|
||||||
eprintln!(" --log-level <LEVEL> debug|verbose|normal|silent");
|
eprintln!(" --log-level <LEVEL> debug|verbose|normal|silent");
|
||||||
eprintln!(" --help, -h Show this help");
|
eprintln!(" --help, -h Show this help");
|
||||||
@@ -210,7 +244,7 @@ mod tests {
|
|||||||
let target = startup_cwd.join("config.toml");
|
let target = startup_cwd.join("config.toml");
|
||||||
std::fs::write(&target, " ").unwrap();
|
std::fs::write(&target, " ").unwrap();
|
||||||
|
|
||||||
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd);
|
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd, true);
|
||||||
assert_eq!(resolved, target.canonicalize().unwrap());
|
assert_eq!(resolved, target.canonicalize().unwrap());
|
||||||
|
|
||||||
let _ = std::fs::remove_file(&target);
|
let _ = std::fs::remove_file(&target);
|
||||||
@@ -226,11 +260,44 @@ mod tests {
|
|||||||
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_path_missing_{nonce}"));
|
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_path_missing_{nonce}"));
|
||||||
std::fs::create_dir_all(&startup_cwd).unwrap();
|
std::fs::create_dir_all(&startup_cwd).unwrap();
|
||||||
|
|
||||||
let resolved = resolve_runtime_config_path("missing.toml", &startup_cwd);
|
let resolved = resolve_runtime_config_path("missing.toml", &startup_cwd, true);
|
||||||
assert_eq!(resolved, startup_cwd.join("missing.toml"));
|
assert_eq!(resolved, startup_cwd.join("missing.toml"));
|
||||||
|
|
||||||
let _ = std::fs::remove_dir(&startup_cwd);
|
let _ = std::fs::remove_dir(&startup_cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolve_runtime_config_path_uses_startup_candidates_when_not_explicit() {
|
||||||
|
let nonce = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_nanos();
|
||||||
|
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_startup_candidates_{nonce}"));
|
||||||
|
std::fs::create_dir_all(&startup_cwd).unwrap();
|
||||||
|
let telemt = startup_cwd.join("telemt.toml");
|
||||||
|
std::fs::write(&telemt, " ").unwrap();
|
||||||
|
|
||||||
|
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd, false);
|
||||||
|
assert_eq!(resolved, telemt.canonicalize().unwrap());
|
||||||
|
|
||||||
|
let _ = std::fs::remove_file(&telemt);
|
||||||
|
let _ = std::fs::remove_dir(&startup_cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolve_runtime_config_path_defaults_to_startup_config_when_none_found() {
|
||||||
|
let nonce = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_nanos();
|
||||||
|
let startup_cwd = std::env::temp_dir().join(format!("telemt_cfg_startup_default_{nonce}"));
|
||||||
|
std::fs::create_dir_all(&startup_cwd).unwrap();
|
||||||
|
|
||||||
|
let resolved = resolve_runtime_config_path("config.toml", &startup_cwd, false);
|
||||||
|
assert_eq!(resolved, startup_cwd.join("config.toml"));
|
||||||
|
|
||||||
|
let _ = std::fs::remove_dir(&startup_cwd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) {
|
pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) {
|
||||||
|
|||||||
+97
-6
@@ -112,6 +112,7 @@ async fn run_inner(
|
|||||||
.await;
|
.await;
|
||||||
let cli_args = parse_cli();
|
let cli_args = parse_cli();
|
||||||
let config_path_cli = cli_args.config_path;
|
let config_path_cli = cli_args.config_path;
|
||||||
|
let config_path_explicit = cli_args.config_path_explicit;
|
||||||
let data_path = cli_args.data_path;
|
let data_path = cli_args.data_path;
|
||||||
let cli_silent = cli_args.silent;
|
let cli_silent = cli_args.silent;
|
||||||
let cli_log_level = cli_args.log_level;
|
let cli_log_level = cli_args.log_level;
|
||||||
@@ -123,7 +124,8 @@ async fn run_inner(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let config_path = resolve_runtime_config_path(&config_path_cli, &startup_cwd);
|
let mut config_path =
|
||||||
|
resolve_runtime_config_path(&config_path_cli, &startup_cwd, config_path_explicit);
|
||||||
|
|
||||||
let mut config = match ProxyConfig::load(&config_path) {
|
let mut config = match ProxyConfig::load(&config_path) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
@@ -133,11 +135,100 @@ async fn run_inner(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
} else {
|
} else {
|
||||||
let default = ProxyConfig::default();
|
let default = ProxyConfig::default();
|
||||||
std::fs::write(&config_path, toml::to_string_pretty(&default).unwrap()).unwrap();
|
|
||||||
eprintln!(
|
let serialized = match toml::to_string_pretty(&default)
|
||||||
"[telemt] Created default config at {}",
|
.or_else(|_| toml::to_string(&default))
|
||||||
config_path.display()
|
{
|
||||||
);
|
Ok(value) => Some(value),
|
||||||
|
Err(serialize_error) => {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Warning: failed to serialize default config: {}",
|
||||||
|
serialize_error
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if config_path_explicit {
|
||||||
|
if let Some(serialized) = serialized.as_ref() {
|
||||||
|
if let Err(write_error) = std::fs::write(&config_path, serialized) {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Error: failed to create explicit config at {}: {}",
|
||||||
|
config_path.display(),
|
||||||
|
write_error
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Created default config at {}",
|
||||||
|
config_path.display()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Warning: running with in-memory default config without writing to disk"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let system_dir = std::path::Path::new("/etc/telemt");
|
||||||
|
let system_config_path = system_dir.join("telemt.toml");
|
||||||
|
let startup_config_path = startup_cwd.join("config.toml");
|
||||||
|
let mut persisted = false;
|
||||||
|
|
||||||
|
if let Some(serialized) = serialized.as_ref() {
|
||||||
|
match std::fs::create_dir_all(system_dir) {
|
||||||
|
Ok(()) => match std::fs::write(&system_config_path, serialized) {
|
||||||
|
Ok(()) => {
|
||||||
|
config_path = system_config_path;
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Created default config at {}",
|
||||||
|
config_path.display()
|
||||||
|
);
|
||||||
|
persisted = true;
|
||||||
|
}
|
||||||
|
Err(write_error) => {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Warning: failed to write default config at {}: {}",
|
||||||
|
system_config_path.display(),
|
||||||
|
write_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(create_error) => {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Warning: failed to create {}: {}",
|
||||||
|
system_dir.display(),
|
||||||
|
create_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !persisted {
|
||||||
|
match std::fs::write(&startup_config_path, serialized) {
|
||||||
|
Ok(()) => {
|
||||||
|
config_path = startup_config_path;
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Created default config at {}",
|
||||||
|
config_path.display()
|
||||||
|
);
|
||||||
|
persisted = true;
|
||||||
|
}
|
||||||
|
Err(write_error) => {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Warning: failed to write default config at {}: {}",
|
||||||
|
startup_config_path.display(),
|
||||||
|
write_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !persisted {
|
||||||
|
eprintln!(
|
||||||
|
"[telemt] Warning: running with in-memory default config without writing to disk"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
default
|
default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,17 @@ async fn render_metrics(stats: &Stats, config: &ProxyConfig, ip_tracker: &UserIp
|
|||||||
let me_allows_normal = telemetry.me_level.allows_normal();
|
let me_allows_normal = telemetry.me_level.allows_normal();
|
||||||
let me_allows_debug = telemetry.me_level.allows_debug();
|
let me_allows_debug = telemetry.me_level.allows_debug();
|
||||||
|
|
||||||
|
let _ = writeln!(
|
||||||
|
out,
|
||||||
|
"# HELP telemt_build_info Build information for the running telemt binary"
|
||||||
|
);
|
||||||
|
let _ = writeln!(out, "# TYPE telemt_build_info gauge");
|
||||||
|
let _ = writeln!(
|
||||||
|
out,
|
||||||
|
"telemt_build_info{{version=\"{}\"}} 1",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
);
|
||||||
|
|
||||||
let _ = writeln!(out, "# HELP telemt_uptime_seconds Proxy uptime");
|
let _ = writeln!(out, "# HELP telemt_uptime_seconds Proxy uptime");
|
||||||
let _ = writeln!(out, "# TYPE telemt_uptime_seconds gauge");
|
let _ = writeln!(out, "# TYPE telemt_uptime_seconds gauge");
|
||||||
let _ = writeln!(out, "telemt_uptime_seconds {:.1}", stats.uptime_secs());
|
let _ = writeln!(out, "telemt_uptime_seconds {:.1}", stats.uptime_secs());
|
||||||
@@ -2903,6 +2914,10 @@ mod tests {
|
|||||||
|
|
||||||
let output = render_metrics(&stats, &config, &tracker).await;
|
let output = render_metrics(&stats, &config, &tracker).await;
|
||||||
|
|
||||||
|
assert!(output.contains(&format!(
|
||||||
|
"telemt_build_info{{version=\"{}\"}} 1",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
)));
|
||||||
assert!(output.contains("telemt_connections_total 2"));
|
assert!(output.contains("telemt_connections_total 2"));
|
||||||
assert!(output.contains("telemt_connections_bad_total 1"));
|
assert!(output.contains("telemt_connections_bad_total 1"));
|
||||||
assert!(output.contains("telemt_handshake_timeouts_total 1"));
|
assert!(output.contains("telemt_handshake_timeouts_total 1"));
|
||||||
@@ -2995,6 +3010,7 @@ mod tests {
|
|||||||
let tracker = UserIpTracker::new();
|
let tracker = UserIpTracker::new();
|
||||||
let config = ProxyConfig::default();
|
let config = ProxyConfig::default();
|
||||||
let output = render_metrics(&stats, &config, &tracker).await;
|
let output = render_metrics(&stats, &config, &tracker).await;
|
||||||
|
assert!(output.contains("# TYPE telemt_build_info gauge"));
|
||||||
assert!(output.contains("# TYPE telemt_uptime_seconds gauge"));
|
assert!(output.contains("# TYPE telemt_uptime_seconds gauge"));
|
||||||
assert!(output.contains("# TYPE telemt_connections_total counter"));
|
assert!(output.contains("# TYPE telemt_connections_total counter"));
|
||||||
assert!(output.contains("# TYPE telemt_connections_bad_total counter"));
|
assert!(output.contains("# TYPE telemt_connections_bad_total counter"));
|
||||||
@@ -3052,6 +3068,14 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.contains("telemt_connections_total 3")
|
.contains("telemt_connections_total 3")
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
std::str::from_utf8(body.as_ref())
|
||||||
|
.unwrap()
|
||||||
|
.contains(&format!(
|
||||||
|
"telemt_build_info{{version=\"{}\"}} 1",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
config.general.beobachten = true;
|
config.general.beobachten = true;
|
||||||
config.general.beobachten_minutes = 10;
|
config.general.beobachten_minutes = 10;
|
||||||
|
|||||||
Reference in New Issue
Block a user