Skip to content

Commit 1a4ca3b

Browse files
committed
Improve resource control and update dependencies
1 parent 16375d9 commit 1a4ca3b

4 files changed

Lines changed: 108 additions & 39 deletions

File tree

Cargo.lock

Lines changed: 48 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ hyper = { version = "1.6.0", default-features = false, features = [
3131
"server",
3232
"http1",
3333
], optional = true }
34-
hyper-util = { version = "0.1.11", features = ["tokio"] }
34+
hyper-util = { version = "0.1.12", features = ["tokio"] }
3535
http-body-util = "0.1.3"
3636
ipext = "0.1.0"
3737
libsodium-sys-stable = "1.22.3"
@@ -44,9 +44,9 @@ rustc-hash = "2.1.1"
4444
serde = "1.0.219"
4545
serde_derive = "1.0.219"
4646
serde-big-array = "0.5.1"
47-
sieve-cache = "0.2.1"
47+
sieve-cache = "1.1.5"
4848
siphasher = "1.0.1"
49-
slabigator = "0.9.3"
49+
slabigator = "0.9.5"
5050
socket2 = "0.5.9"
5151
tokio = { version = "1.45.0", features = [
5252
"net",

src/main.rs

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,16 @@ async fn encrypt_and_respond_to_query(
148148
ClientCtx::Udp(_) => original_packet_size,
149149
ClientCtx::Tcp(_) => DNSCRYPT_TCP_RESPONSE_MAX_SIZE,
150150
};
151-
let response = match &shared_key {
152-
None => response,
153-
Some(shared_key) => dnscrypt::encrypt(
151+
let response = match (&shared_key, &nonce) {
152+
(None, _) => response,
153+
(Some(_), None) => {
154+
warn!("Shared key provided without nonce");
155+
bail!("Internal error: shared key without nonce");
156+
},
157+
(Some(shared_key), Some(nonce)) => dnscrypt::encrypt(
154158
maybe_truncate_response(&client_ctx, packet, response, original_packet_size)?,
155159
shared_key,
156-
nonce.as_ref().unwrap(),
160+
nonce,
157161
max_response_size,
158162
)?,
159163
};
@@ -300,10 +304,31 @@ async fn tcp_acceptor(globals: Arc<Globals>, tcp_listener: TcpListener) -> Resul
300304
if e.kind() == std::io::ErrorKind::WouldBlock {
301305
continue;
302306
}
303-
warn!("TCP accept error: {}", e);
304-
if let Some(tx_oldest) = active_connections.lock().pop_back() {
305-
let _ = tx_oldest.send(());
307+
error!("TCP accept error: {}", e);
308+
309+
// Rate limit repeated errors to avoid spinning
310+
let is_resource_error = matches!(
311+
e.kind(),
312+
std::io::ErrorKind::ConnectionRefused |
313+
std::io::ErrorKind::ConnectionReset |
314+
std::io::ErrorKind::ConnectionAborted |
315+
std::io::ErrorKind::AddrInUse |
316+
std::io::ErrorKind::AddrNotAvailable
317+
);
318+
319+
if is_resource_error {
320+
// For resource-related errors, try to free up connections
321+
let mut connections = active_connections.lock();
322+
let freed = connections.len().min(5);
323+
for _ in 0..freed {
324+
if let Some(tx_oldest) = connections.pop_back() {
325+
let _ = tx_oldest.send(());
326+
}
327+
}
328+
info!("Freed {} connections to recover from resource error", freed);
306329
}
330+
331+
// Add delay to prevent CPU spinning on persistent errors
307332
tokio::time::sleep(Duration::from_secs(1)).await;
308333
continue;
309334
}
@@ -384,8 +409,28 @@ async fn udp_acceptor(
384409
if packet_len < DNS_HEADER_SIZE {
385410
continue;
386411
}
387-
let net_udp_socket = net_udp_socket.try_clone()?;
412+
// Create a socket clone only when we've checked the packet is valid
413+
// This helps avoid resource exhaustion
388414
packet.truncate(packet_len);
415+
416+
// Only create a new socket if there's capacity for a new connection
417+
let active_count = concurrent_connections.load(Ordering::Relaxed);
418+
if active_count >= globals.udp_max_active_connections {
419+
debug!("UDP connection limit reached, dropping packet");
420+
continue;
421+
}
422+
423+
// Clone the socket for this request
424+
let net_udp_socket = match net_udp_socket.try_clone() {
425+
Ok(socket) => socket,
426+
Err(e) => {
427+
error!("Failed to clone UDP socket: {}", e);
428+
// Add a small delay to avoid spinning on socket errors
429+
tokio::time::sleep(Duration::from_millis(100)).await;
430+
continue;
431+
}
432+
};
433+
389434
let client_ctx = ClientCtx::Udp(UdpClientCtx {
390435
net_udp_socket,
391436
client_addr,

src/resolver.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::hash::Hasher;
33
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
44

55
use byteorder::{BigEndian, ByteOrder};
6-
use rand::random;
6+
use rand::{random, Rng, rng};
77
use siphasher::sip128::Hasher128;
88
use tokio::io::{AsyncReadExt, AsyncWriteExt};
99
use tokio::net::{TcpSocket, UdpSocket};
@@ -224,7 +224,9 @@ pub async fn get_cached_response_or_resolve(
224224
let original_tid = dns::tid(packet);
225225
dns::set_tid(packet, 0);
226226
dns::normalize_qname(packet)?;
227-
let mut hasher = globals.hasher;
227+
// Create a new hasher instance to avoid race conditions
228+
let (sh_k0, sh_k1) = rng().random::<(u64, u64)>();
229+
let mut hasher = siphasher::sip128::SipHasher13::new_with_keys(sh_k0, sh_k1);
228230
hasher.write(packet);
229231
let packet_hash = hasher.finish128().as_u128();
230232
let cached_response = {

0 commit comments

Comments
 (0)