Update dependencies and refactor random number generation

- Bump versions of several dependencies in Cargo.toml for improved functionality and security, including:
  - socket2 to 0.6
  - nix to 0.31
  - toml to 1.0
  - x509-parser to 0.18
  - dashmap to 6.1
  - rand to 0.10
  - reqwest to 0.13
  - notify to 8.2
  - ipnetwork to 0.21
  - webpki-roots to 1.0
  - criterion to 0.8
- Introduce `OnceLock` for secure random number generation in multiple modules to ensure thread safety and reduce overhead.
- Refactor random number generation calls to use the new `RngExt` trait methods for consistency and clarity.
- Add new PNG files for architectural documentation.
This commit is contained in:
David Osipov 2026-03-21 15:43:07 +04:00
parent b930ea1ec5
commit c8632de5b6
No known key found for this signature in database
GPG Key ID: 0E55C4A47454E82E
26 changed files with 507 additions and 305 deletions

713
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -26,15 +26,15 @@ subtle = "2.6"
static_assertions = "1.1"
# Network
socket2 = { version = "0.5", features = ["all"] }
nix = { version = "0.28", default-features = false, features = ["net"] }
socket2 = { version = "0.6", features = ["all"] }
nix = { version = "0.31", default-features = false, features = ["net"] }
shadowsocks = { version = "1.24", features = ["aead-cipher-2022"] }
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
x509-parser = "0.15"
toml = "1.0"
x509-parser = "0.18"
# Utils
bytes = "1.9"
@ -42,10 +42,10 @@ thiserror = "2.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
parking_lot = "0.12"
dashmap = "5.5"
dashmap = "6.1"
arc-swap = "1.7"
lru = "0.16"
rand = "0.9"
rand = "0.10"
chrono = { version = "0.4", features = ["serde"] }
hex = "0.4"
base64 = "0.22"
@ -58,20 +58,20 @@ x25519-dalek = "2"
anyhow = "1.0"
# HTTP
reqwest = { version = "0.12", features = ["rustls-tls"], default-features = false }
notify = { version = "6", features = ["macos_fsevent"] }
ipnetwork = "0.20"
reqwest = { version = "0.13", features = ["rustls"], default-features = false }
notify = "8.2"
ipnetwork = { version = "0.21", features = ["serde"] }
hyper = { version = "1", features = ["server", "http1"] }
hyper-util = { version = "0.1", features = ["tokio", "server-auto"] }
http-body-util = "0.1"
httpdate = "1.0"
tokio-rustls = { version = "0.26", default-features = false, features = ["tls12"] }
rustls = { version = "0.23", default-features = false, features = ["std", "tls12", "ring"] }
webpki-roots = "0.26"
webpki-roots = "1.0"
[dev-dependencies]
tokio-test = "0.4"
criterion = "0.5"
criterion = "0.8"
proptest = "1.4"
futures = "0.3"

BIN
docs/model/FakeTLS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

BIN
docs/model/architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 KiB

View File

@ -1,10 +1,12 @@
use std::net::IpAddr;
use std::sync::OnceLock;
use chrono::{DateTime, Utc};
use hyper::StatusCode;
use rand::Rng;
use serde::{Deserialize, Serialize};
use crate::crypto::SecureRandom;
const MAX_USERNAME_LEN: usize = 64;
#[derive(Debug)]
@ -482,7 +484,9 @@ pub(super) fn is_valid_username(user: &str) -> bool {
}
pub(super) fn random_user_secret() -> String {
static API_SECRET_RNG: OnceLock<SecureRandom> = OnceLock::new();
let rng = API_SECRET_RNG.get_or_init(SecureRandom::new);
let mut bytes = [0u8; 16];
rand::rng().fill(&mut bytes);
rng.fill(&mut bytes);
hex::encode(bytes)
}

View File

@ -3,7 +3,7 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use rand::Rng;
use rand::RngExt;
/// Options for the init command
pub struct InitOptions {

View File

@ -5,7 +5,7 @@ use std::hash::{DefaultHasher, Hash, Hasher};
use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf};
use rand::Rng;
use rand::RngExt;
use serde::{Deserialize, Serialize};
use shadowsocks::config::ServerConfig as ShadowsocksServerConfig;
use tracing::warn;
@ -979,7 +979,7 @@ impl ProxyConfig {
if !config.censorship.tls_emulation
&& config.censorship.fake_cert_len == default_fake_cert_len()
{
config.censorship.fake_cert_len = rand::rng().gen_range(1024..4096);
config.censorship.fake_cert_len = rand::rng().random_range(1024..4096);
}
// Resolve listen_tcp: explicit value wins, otherwise auto-detect.

View File

@ -3,7 +3,7 @@
#![allow(deprecated)]
#![allow(dead_code)]
use rand::{Rng, RngCore, SeedableRng};
use rand::{Rng, RngExt, SeedableRng};
use rand::rngs::StdRng;
use parking_lot::Mutex;
use zeroize::Zeroize;
@ -101,7 +101,7 @@ impl SecureRandom {
return 0;
}
let mut inner = self.inner.lock();
inner.rng.gen_range(0..max)
inner.rng.random_range(0..max)
}
/// Generate random bits
@ -141,7 +141,7 @@ impl SecureRandom {
pub fn shuffle<T>(&self, slice: &mut [T]) {
let mut inner = self.inner.lock();
for i in (1..slice.len()).rev() {
let j = inner.rng.gen_range(0..=i);
let j = inner.rng.random_range(0..=i);
slice.swap(i, j);
}
}

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use std::time::Duration;
use rand::Rng;
use rand::RngExt;
use tracing::warn;
use crate::config::ProxyConfig;

View File

@ -2,13 +2,20 @@
#![allow(dead_code)]
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::sync::OnceLock;
use tokio::net::{lookup_host, UdpSocket};
use tokio::time::{timeout, Duration, sleep};
use crate::crypto::SecureRandom;
use crate::error::{ProxyError, Result};
use crate::network::dns_overrides::{resolve, split_host_port};
fn stun_rng() -> &'static SecureRandom {
static STUN_RNG: OnceLock<SecureRandom> = OnceLock::new();
STUN_RNG.get_or_init(SecureRandom::new)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IpFamily {
V4,
@ -49,8 +56,6 @@ pub async fn stun_probe_family_with_bind(
family: IpFamily,
bind_ip: Option<IpAddr>,
) -> Result<Option<StunProbeResult>> {
use rand::RngCore;
let bind_addr = match (family, bind_ip) {
(IpFamily::V4, Some(IpAddr::V4(ip))) => SocketAddr::new(IpAddr::V4(ip), 0),
(IpFamily::V6, Some(IpAddr::V6(ip))) => SocketAddr::new(IpAddr::V6(ip), 0),
@ -88,7 +93,7 @@ pub async fn stun_probe_family_with_bind(
req[0..2].copy_from_slice(&0x0001u16.to_be_bytes()); // Binding Request
req[2..4].copy_from_slice(&0u16.to_be_bytes()); // length
req[4..8].copy_from_slice(&0x2112A442u32.to_be_bytes()); // magic cookie
rand::rng().fill_bytes(&mut req[8..20]); // transaction ID
stun_rng().fill(&mut req[8..20]); // transaction ID
let mut buf = [0u8; 256];
let mut attempt = 0;

View File

@ -8,7 +8,7 @@ use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use ipnetwork::IpNetwork;
use rand::Rng;
use rand::RngExt;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite};
use tokio::net::TcpStream;
use tokio::time::timeout;

View File

@ -17,7 +17,7 @@ use tracing::{debug, warn, trace};
use zeroize::{Zeroize, Zeroizing};
use crate::crypto::{sha256, AesCtr, SecureRandom};
use rand::Rng;
use rand::RngExt;
use crate::protocol::constants::*;
use crate::protocol::tls;
use crate::stream::{FakeTlsReader, FakeTlsWriter, CryptoReader, CryptoWriter};

View File

@ -3,7 +3,7 @@
use std::str;
use std::net::SocketAddr;
use std::time::Duration;
use rand::{Rng, RngCore};
use rand::{Rng, RngExt};
use tokio::net::TcpStream;
#[cfg(unix)]
use tokio::net::UnixStream;

View File

@ -1,7 +1,7 @@
use super::*;
use crate::crypto::{sha256, sha256_hmac};
use dashmap::DashMap;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use rand::rngs::StdRng;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;

View File

@ -10,7 +10,7 @@ use crate::stats::Stats;
use crate::stream::{BufferPool, CryptoReader, CryptoWriter, PooledBuffer};
use crate::transport::middle_proxy::MePool;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
use std::sync::Arc;

View File

@ -3,7 +3,7 @@ use crate::error::ProxyError;
use crate::stats::Stats;
use crate::stream::BufferPool;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use std::sync::Arc;
use tokio::io::{duplex, AsyncRead, AsyncReadExt, AsyncWriteExt};
use tokio::time::{timeout, Duration, Instant};

View File

@ -3,7 +3,7 @@ use crate::error::ProxyError;
use crate::stats::Stats;
use crate::stream::BufferPool;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use std::sync::Arc;
use tokio::io::{duplex, AsyncRead, AsyncReadExt, AsyncWriteExt};
use tokio::sync::Barrier;

View File

@ -733,7 +733,7 @@ async fn relay_bidirectional_asymmetric_backpressure() {
);
}
use rand::{Rng, SeedableRng, rngs::StdRng};
use rand::{RngExt, SeedableRng, rngs::StdRng};
#[tokio::test]
async fn relay_bidirectional_light_fuzzing_temporal_jitter() {

View File

@ -1,6 +1,6 @@
use super::*;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use std::sync::Arc;
#[test]

View File

@ -1,5 +1,5 @@
use super::*;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use rand::rngs::StdRng;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};

View File

@ -4,7 +4,7 @@ use std::net::SocketAddr;
use std::sync::Arc;
use std::time::{Duration, Instant};
use rand::Rng;
use rand::RngExt;
use tracing::{debug, info, warn};
use crate::config::MeFloorMode;

View File

@ -2,7 +2,7 @@ use std::collections::HashSet;
use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use rand::Rng;
use rand::RngExt;
use rand::seq::SliceRandom;
use tracing::{debug, info, warn};

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;
use rand::Rng;
use rand::RngExt;
use rand::seq::SliceRandom;
use tracing::{debug, info, warn};
use std::collections::hash_map::DefaultHasher;

View File

@ -6,7 +6,7 @@ use std::io::ErrorKind;
use bytes::Bytes;
use bytes::BytesMut;
use rand::Rng;
use rand::RngExt;
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
use tracing::{debug, info, warn};

View File

@ -23,7 +23,7 @@ pub fn configure_tcp_socket(
let socket = socket2::SockRef::from(stream);
// Disable Nagle's algorithm for lower latency
socket.set_nodelay(true)?;
socket.set_tcp_nodelay(true)?;
// Set keepalive if enabled
if keepalive {
@ -54,7 +54,7 @@ pub fn configure_client_socket(
let socket = socket2::SockRef::from(stream);
// Disable Nagle's algorithm
socket.set_nodelay(true)?;
socket.set_tcp_nodelay(true)?;
// Set keepalive
let keepalive = TcpKeepalive::new()
@ -129,7 +129,7 @@ pub fn create_outgoing_socket_bound(addr: SocketAddr, bind_addr: Option<IpAddr>)
socket.set_nonblocking(true)?;
// Disable Nagle
socket.set_nodelay(true)?;
socket.set_tcp_nodelay(true)?;
socket.set_recv_buffer_size(DEFAULT_SOCKET_BUFFER_BYTES)?;
socket.set_send_buffer_size(DEFAULT_SOCKET_BUFFER_BYTES)?;

View File

@ -4,7 +4,7 @@
#![allow(deprecated)]
use rand::Rng;
use rand::RngExt;
use std::collections::{BTreeSet, HashMap};
use std::net::{IpAddr, SocketAddr};
use std::pin::Pin;
@ -600,7 +600,7 @@ impl UpstreamManager {
return self.connect_retry_backoff;
}
let jitter_cap_ms = (base_ms / 2).max(1);
let jitter_ms = rand::rng().gen_range(0..=jitter_cap_ms);
let jitter_ms = rand::rng().random_range(0..=jitter_cap_ms);
Duration::from_millis(base_ms.saturating_add(jitter_ms))
}
@ -667,7 +667,7 @@ impl UpstreamManager {
"No healthy upstreams available! Using random."
);
}
return Some(filtered_upstreams[rand::rng().gen_range(0..filtered_upstreams.len())]);
return Some(filtered_upstreams[rand::rng().random_range(0..filtered_upstreams.len())]);
}
if healthy.len() == 1 {
@ -690,10 +690,10 @@ impl UpstreamManager {
let total: f64 = weights.iter().map(|(_, w)| w).sum();
if total <= 0.0 {
return Some(healthy[rand::rng().gen_range(0..healthy.len())]);
return Some(healthy[rand::rng().random_range(0..healthy.len())]);
}
let mut choice: f64 = rand::rng().gen_range(0.0..total);
let mut choice: f64 = rand::rng().random_range(0.0..total);
for &(idx, weight) in &weights {
if choice < weight {