11use anyhow:: { anyhow, Result } ;
22use base64:: { engine:: general_purpose:: STANDARD as BASE64 , Engine as _} ;
33use rand:: seq:: SliceRandom ;
4- use std:: net:: { Ipv4Addr , Ipv6Addr , SocketAddr } ;
4+ use std:: collections:: HashMap ;
5+ use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr } ;
56use std:: str:: FromStr ;
6- use std:: sync:: Arc ;
7+ use std:: sync:: { Arc , Mutex } ;
8+ use std:: time:: Instant ;
79use tokio:: net:: UdpSocket ;
810use tokio:: sync:: broadcast;
9- use tracing:: { error, info} ;
11+ use tracing:: { error, info, warn } ;
1012use trust_dns_proto:: op:: { Message , MessageType , OpCode , Query , ResponseCode } ;
1113use trust_dns_proto:: rr:: rdata:: { CNAME , MX , TXT } ;
1214use trust_dns_proto:: rr:: { Name , RData , Record , RecordType } ;
@@ -18,6 +20,54 @@ use crate::models::{CacheMessage, DnsRequestLog};
1820use crate :: utils:: config:: CONFIG ;
1921use crate :: utils:: { generate_request_id, get_current_timestamp, get_subdomain_from_hostname} ;
2022
23+ /// Maximum UDP DNS response size (RFC 1035)
24+ const MAX_UDP_RESPONSE_SIZE : usize = 512 ;
25+
26+ /// Maximum DNS queries per second per IP
27+ const DNS_RATE_LIMIT_PER_SECOND : u32 = 100 ;
28+
29+ /// Rate limiter cleanup interval (number of queries between cleanups)
30+ const RATE_LIMITER_CLEANUP_INTERVAL : u64 = 1000 ;
31+
32+ struct DnsRateLimiter {
33+ limits : Mutex < HashMap < IpAddr , ( Instant , u32 ) > > ,
34+ max_per_second : u32 ,
35+ }
36+
37+ impl DnsRateLimiter {
38+ fn new ( max_per_second : u32 ) -> Self {
39+ Self {
40+ limits : Mutex :: new ( HashMap :: new ( ) ) ,
41+ max_per_second,
42+ }
43+ }
44+
45+ /// Returns true if the request should be allowed, false if rate-limited.
46+ fn check ( & self , ip : IpAddr ) -> bool {
47+ let mut limits = self . limits . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
48+ let now = Instant :: now ( ) ;
49+
50+ let entry = limits. entry ( ip) . or_insert ( ( now, 0 ) ) ;
51+
52+ // If more than 1 second has passed, reset the counter
53+ if now. duration_since ( entry. 0 ) . as_secs ( ) >= 1 {
54+ entry. 0 = now;
55+ entry. 1 = 1 ;
56+ true
57+ } else {
58+ entry. 1 += 1 ;
59+ entry. 1 <= self . max_per_second
60+ }
61+ }
62+
63+ /// Remove stale entries older than 10 seconds
64+ fn cleanup ( & self ) {
65+ let mut limits = self . limits . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
66+ let now = Instant :: now ( ) ;
67+ limits. retain ( |_, ( instant, _) | now. duration_since ( * instant) . as_secs ( ) < 10 ) ;
68+ }
69+ }
70+
2171pub struct Server {
2272 cache : Arc < Cache > ,
2373 tx : Arc < broadcast:: Sender < CacheMessage > > ,
@@ -33,10 +83,24 @@ impl Server {
3383
3484 let socket = Arc :: new ( UdpSocket :: bind ( format ! ( "0.0.0.0:{}" , CONFIG . dns_port) ) . await ?) ;
3585 let mut buf = vec ! [ 0u8 ; 512 ] ;
86+ let rate_limiter = Arc :: new ( DnsRateLimiter :: new ( DNS_RATE_LIMIT_PER_SECOND ) ) ;
87+ let mut query_count: u64 = 0 ;
3688
3789 loop {
3890 match socket. recv_from ( & mut buf) . await {
3991 Ok ( ( len, addr) ) => {
92+ // Rate limit check
93+ if !rate_limiter. check ( addr. ip ( ) ) {
94+ warn ! ( "DNS rate limit exceeded for {}" , addr. ip( ) ) ;
95+ continue ;
96+ }
97+
98+ // Periodic cleanup of stale rate limiter entries
99+ query_count += 1 ;
100+ if query_count. is_multiple_of ( RATE_LIMITER_CLEANUP_INTERVAL ) {
101+ rate_limiter. cleanup ( ) ;
102+ }
103+
40104 let data = buf[ ..len] . to_vec ( ) ;
41105 let cache = self . cache . clone ( ) ;
42106 let tx = self . tx . clone ( ) ;
@@ -97,6 +161,22 @@ async fn handle_dns_request(
97161 let response_bytes = response
98162 . to_bytes ( )
99163 . map_err ( |e| anyhow ! ( "Failed to serialize DNS response: {}" , e) ) ?;
164+
165+ // If UDP response exceeds 512 bytes, set TC (truncation) bit and strip answers
166+ // This forces the client to retry via TCP (which is not spoofable)
167+ let response_bytes = if response_bytes. len ( ) > MAX_UDP_RESPONSE_SIZE {
168+ let mut truncated = response. clone ( ) ;
169+ truncated. set_truncated ( true ) ;
170+ // Remove all answers to fit within 512 bytes - keep only header + question
171+ let empty_answers: Vec < Record > = Vec :: new ( ) ;
172+ truncated. insert_answers ( empty_answers) ;
173+ truncated
174+ . to_bytes ( )
175+ . map_err ( |e| anyhow ! ( "Failed to serialize truncated DNS response: {}" , e) ) ?
176+ } else {
177+ response_bytes
178+ } ;
179+
100180 socket. send_to ( & response_bytes, addr) . await ?;
101181
102182 Ok ( ( ) )
@@ -201,20 +281,9 @@ async fn log_dns_request(
201281
202282 let request_json = serde_json:: to_string ( & request_log) ?;
203283
204- // Push request to list and get the new length to calculate the correct index
284+ // Push request to list
205285 let list_key = format ! ( "requests:{subdomain}" ) ;
206- let index = cache
207- . rpush ( & list_key, & request_json)
208- . await ?
209- . saturating_sub ( 1 ) ;
210-
211- // Store the index for this request ID (used by delete endpoint)
212- cache
213- . set (
214- & format ! ( "request:{subdomain}:{request_id}" ) ,
215- & index. to_string ( ) ,
216- )
217- . await ?;
286+ cache. rpush ( & list_key, & request_json) . await ?;
218287
219288 let message = CacheMessage {
220289 cmd : "new_request" . to_string ( ) ,
0 commit comments