44//! Buffer sizes and max concurrency are configurable via [`ServerConfig`].
55
66use crate :: dns:: Message ;
7+ use crate :: dns:: types:: { RecordClass , RecordType } ;
78use crate :: server:: { RequestHandler , Server , ServerConfig } ;
89use crate :: { Error , Result } ;
910use std:: sync:: Arc ;
1011use tokio:: net:: UdpSocket ;
1112use tokio:: sync:: Semaphore ;
1213use tracing:: { debug, error, info, trace, warn} ;
1314
15+ /// Floor for the read buffer: queries can legally exceed the 512-byte
16+ /// classic limit once EDNS is in play.
17+ const RECV_BUF_MIN : usize = 4096 ;
18+
1419/// UDP DNS server.
1520///
1621/// Each instance binds one UDP socket and dispatches incoming queries to
@@ -199,7 +204,7 @@ impl UdpServer {
199204 ///
200205 /// This method does not panic under normal circumstances.
201206 pub async fn run ( & self ) -> Result < ( ) > {
202- let mut buf = vec ! [ 0u8 ; self . config. max_udp_size] ;
207+ let mut buf = vec ! [ 0u8 ; self . config. max_udp_size. max ( RECV_BUF_MIN ) ] ;
203208
204209 info ! ( "UDP server started" ) ;
205210
@@ -266,6 +271,7 @@ impl UdpServer {
266271
267272 // Create request context
268273 let req_id = request. id ( ) ;
274+ let max_response = client_udp_limit ( & request) ;
269275 let ctx = crate :: server:: RequestContext :: with_client (
270276 request,
271277 Some ( peer_addr) ,
@@ -285,6 +291,7 @@ impl UdpServer {
285291
286292 // Serialize and send response
287293 let response_data = Self :: serialize_response ( & response) ?;
294+ let response_data = fit_udp_response ( & response, response_data, max_response) ;
288295
289296 socket
290297 . send_to ( & response_data, peer_addr)
@@ -304,13 +311,56 @@ impl UdpServer {
304311
305312 /// Serialize DNS response to wire format.
306313 ///
307- /// Thin wrapper over [`crate::dns::wire::serialize_message`]; returns an error
308- /// on invalid names/labels or messages exceeding the UDP size limit .
314+ /// Thin wrapper over [`crate::dns::wire::serialize_message`]; returns an
315+ /// error on invalid names/labels.
309316 fn serialize_response ( message : & Message ) -> Result < Vec < u8 > > {
310317 crate :: dns:: wire:: serialize_message ( message)
311318 }
312319}
313320
321+ /// Response size limit for `request`: the client's advertised EDNS payload
322+ /// size, or the classic 512-byte limit without EDNS. Sizes below 512 are
323+ /// ignored (RFC 6891).
324+ fn client_udp_limit ( request : & Message ) -> usize {
325+ request
326+ . additional ( )
327+ . iter ( )
328+ . find ( |rr| rr. rtype ( ) == RecordType :: OPT )
329+ . and_then ( |rr| match rr. rclass ( ) {
330+ RecordClass :: Unknown ( size) if size >= 512 => Some ( size as usize ) ,
331+ _ => None ,
332+ } )
333+ . unwrap_or ( 512 )
334+ }
335+
336+ /// Send `serialized` as-is while it fits; past the limit, fall back to a
337+ /// TC=1 header plus question section so the client retries over TCP.
338+ fn fit_udp_response ( response : & Message , serialized : Vec < u8 > , limit : usize ) -> Vec < u8 > {
339+ if serialized. len ( ) <= limit {
340+ return serialized;
341+ }
342+
343+ let mut truncated = response. clone ( ) ;
344+ truncated. set_truncated ( true ) ;
345+ truncated. answers_mut ( ) . clear ( ) ;
346+ truncated. authority_mut ( ) . clear ( ) ;
347+ truncated. additional_mut ( ) . clear ( ) ;
348+ match crate :: dns:: wire:: serialize_message ( & truncated) {
349+ Ok ( data) => {
350+ debug ! (
351+ limit,
352+ full_len = serialized. len( ) ,
353+ truncated_len = data. len( ) ,
354+ "response exceeds UDP limit, sending TC=1"
355+ ) ;
356+ data
357+ }
358+ // cannot happen for a message that just serialized, but never
359+ // fail to answer over a truncation fallback
360+ Err ( _) => serialized,
361+ }
362+ }
363+
314364#[ async_trait:: async_trait]
315365impl Server for UdpServer {
316366 async fn from_config ( config : ServerConfig ) -> Result < Self > {
@@ -452,4 +502,83 @@ mod tests {
452502 assert ! ( parsed. is_response( ) ) ;
453503 assert ! ( parsed. recursion_available( ) ) ;
454504 }
505+
506+ #[ test]
507+ fn test_client_udp_limit ( ) {
508+ let mut req = Message :: new ( ) ;
509+ req. set_query ( true ) ;
510+ req. add_question ( Question :: new ( "example.com" , RecordType :: A , RecordClass :: IN ) ) ;
511+
512+ // no EDNS: classic 512-byte limit
513+ assert_eq ! ( client_udp_limit( & req) , 512 ) ;
514+
515+ req. add_additional ( crate :: dns:: ResourceRecord :: new (
516+ "." ,
517+ RecordType :: OPT ,
518+ RecordClass :: from_u16 ( 1232 ) ,
519+ 0 ,
520+ crate :: dns:: RData :: OPT {
521+ extended_rcode : 0 ,
522+ version : 0 ,
523+ flags : 0 ,
524+ options : Vec :: new ( ) ,
525+ } ,
526+ ) ) ;
527+ assert_eq ! ( client_udp_limit( & req) , 1232 ) ;
528+
529+ // sizes below 512 are invalid per RFC 6891 and must not shrink the limit
530+ let mut tiny = Message :: new ( ) ;
531+ tiny. add_question ( Question :: new ( "example.com" , RecordType :: A , RecordClass :: IN ) ) ;
532+ tiny. add_additional ( crate :: dns:: ResourceRecord :: new (
533+ "." ,
534+ RecordType :: OPT ,
535+ RecordClass :: from_u16 ( 100 ) ,
536+ 0 ,
537+ crate :: dns:: RData :: OPT {
538+ extended_rcode : 0 ,
539+ version : 0 ,
540+ flags : 0 ,
541+ options : Vec :: new ( ) ,
542+ } ,
543+ ) ) ;
544+ assert_eq ! ( client_udp_limit( & tiny) , 512 ) ;
545+ }
546+
547+ #[ test]
548+ fn test_fit_udp_response_truncates ( ) {
549+ let mut resp = Message :: new ( ) ;
550+ resp. set_id ( 0x77 ) ;
551+ resp. set_response ( true ) ;
552+ resp. add_question ( Question :: new (
553+ "big.example.com" ,
554+ RecordType :: A ,
555+ RecordClass :: IN ,
556+ ) ) ;
557+ for i in 0 ..40 {
558+ resp. add_answer ( crate :: dns:: ResourceRecord :: new (
559+ "big.example.com" ,
560+ RecordType :: A ,
561+ RecordClass :: IN ,
562+ 300 ,
563+ crate :: dns:: RData :: A ( std:: net:: Ipv4Addr :: new ( 192 , 0 , 2 , i as u8 ) ) ,
564+ ) ) ;
565+ }
566+
567+ let full = UdpServer :: serialize_response ( & resp) . unwrap ( ) ;
568+ assert ! ( full. len( ) > 512 ) ;
569+
570+ let out = fit_udp_response ( & resp, full. clone ( ) , 512 ) ;
571+ assert ! ( out. len( ) <= 512 , "truncated response must fit the limit" ) ;
572+ let parsed = wire:: parse_message ( & out) . unwrap ( ) ;
573+ assert ! ( parsed. is_truncated( ) , "TC bit must be set" ) ;
574+ assert_eq ! ( parsed. answer_count( ) , 0 ) ;
575+
576+ // a fitting message goes out untouched
577+ let mut small = Message :: new ( ) ;
578+ small. set_id ( 0x78 ) ;
579+ small. set_response ( true ) ;
580+ let small_data = UdpServer :: serialize_response ( & small) . unwrap ( ) ;
581+ let same = fit_udp_response ( & small, small_data. clone ( ) , 512 ) ;
582+ assert_eq ! ( same, small_data) ;
583+ }
455584}
0 commit comments