5050#define HTTP2_MODE 1 /* Change to '1' once implemented */
5151#define OCSP_RESPONDER 1
5252
53- /* Memory pool sizes */
53+ /*
54+ * Memory pool sizes:
55+ *
56+ * MAX_CLIENTS and MAX_REQUESTS define the upper bounds for the internal
57+ * connection and request pools. These limits are used to cap memory usage
58+ * and reduce dynamic allocations under high load.
59+ *
60+ * The default values (1024 clients, 4096 requests) are chosen as a
61+ * reasonable compromise between memory footprint and concurrency for
62+ * typical deployments. If you need to tune these for a specific
63+ * environment (e.g. very small embedded systems or large-scale servers),
64+ * you can adjust the values at compile time.
65+ */
5466#define MAX_CLIENTS 1024
5567#define MAX_REQUESTS 4096
5668
69+ /* DNS request timeout in milliseconds */
70+ #define DNS_REQUEST_TIMEOUT_MS 5000
71+
5772#define IP6_LOCALHOST { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }
5873#define DOHD_REQ_MIN 20
5974#define STR_HTTP2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
@@ -277,9 +292,12 @@ static void dohd_destroy_request(struct req_slot *req);
277292static mempool_t * client_pool = NULL ;
278293static mempool_t * request_pool = NULL ;
279294
280- /* Client hash table for O(1) lookup by fd */
281- #define CLIENT_HASH_BITS 10
282- #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) /* 1024 buckets */
295+ /*
296+ * Client hash table for O(1) lookup by fd.
297+ * Size is 2x MAX_CLIENTS to maintain low load factor and minimize collisions.
298+ * Using power-of-2 size enables fast modulo via bitmask.
299+ */
300+ #define CLIENT_HASH_SIZE (MAX_CLIENTS * 2) /* 2x capacity for low collisions */
283301#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1)
284302
285303static struct client_data * client_hash_table [CLIENT_HASH_SIZE ];
@@ -475,7 +493,7 @@ struct req_slot *dns_create_request_h2(struct client_data *cd, uint32_t stream_i
475493 }
476494 req = mempool_alloc (request_pool );
477495 if (req == NULL ) {
478- dohprint (DOH_ERR , "Failed to allocate memory for a new DNS request." );
496+ dohprint (DOH_ERR , "Request pool exhausted (capacity: %u)" , MAX_REQUESTS );
479497 return req ;
480498 }
481499 req -> resolver = next_resolver ();
@@ -488,10 +506,18 @@ struct req_slot *dns_create_request_h2(struct client_data *cd, uint32_t stream_i
488506 req -> dns_sd = socket (((struct sockaddr_in * )req -> resolver )-> sin_family ,
489507 SOCK_DGRAM , 0 );
490508 if (req -> dns_sd < 0 ) {
491- dohprint (DOH_ERR , "Failed to create traditional DNS socket to forward the request. " );
509+ dohprint (DOH_ERR , "Failed to create UDP socket for DNS request" );
492510 mempool_free (request_pool , req );
493511 return NULL ;
494512 }
513+
514+ /* Set socket receive timeout to complement timer-based timeout.
515+ * This ensures recv() won't block indefinitely if the event loop
516+ * callback fires but data isn't immediately available. */
517+ struct timeval tv = { .tv_sec = DNS_REQUEST_TIMEOUT_MS / 1000 ,
518+ .tv_usec = (DNS_REQUEST_TIMEOUT_MS % 1000 ) * 1000 };
519+ setsockopt (req -> dns_sd , SOL_SOCKET , SO_RCVTIMEO , & tv , sizeof (tv ));
520+
495521 /* append to list */
496522 req -> next = cd -> list ;
497523 cd -> list = req ;
@@ -509,29 +535,31 @@ struct req_slot *dns_create_request_h2(struct client_data *cd, uint32_t stream_i
509535 return req ;
510536}
511537
512- /* DNS request timeout in milliseconds (5 seconds) */
513- #define DNS_REQUEST_TIMEOUT_MS 5000
514-
515538/* Timeout callback - upstream DNS didn't respond in time */
516539static void dns_request_timeout (void * arg )
517540{
518541 struct req_slot * req = (struct req_slot * )arg ;
519542 struct client_data * cd ;
543+ nghttp2_session * session ;
520544
521- dohprint (DOH_WARN , "DNS request timeout (stream %u)" , req -> h2_stream_id );
522545 DOH_Stats .socket_errors ++ ;
523546
524547 /* Use owner_fd to safely look up if client still exists without
525- * dereferencing the potentially-freed owner pointer */
548+ * dereferencing the potentially-freed owner pointer.
549+ * Note: This is single-threaded, so no race between lookup and use. */
526550 cd = client_hash_lookup (req -> owner_fd );
527- if (cd && cd == req -> owner && cd -> h2_session ) {
528- /* Send HTTP 504 Gateway Timeout to client */
529- nghttp2_nv nva [] = {
530- MAKE_NV (":status" , "504" ),
531- MAKE_NV ("server" , "dohd" ),
532- };
533- nghttp2_submit_response (cd -> h2_session , req -> h2_stream_id , nva , 2 , NULL );
534- nghttp2_session_send (cd -> h2_session );
551+ if (cd && cd == req -> owner ) {
552+ /* Capture session pointer after validation */
553+ session = cd -> h2_session ;
554+ if (session ) {
555+ /* Send HTTP 504 Gateway Timeout to client */
556+ nghttp2_nv nva [] = {
557+ MAKE_NV (":status" , "504" ),
558+ MAKE_NV ("server" , "dohd" ),
559+ };
560+ nghttp2_submit_response (session , req -> h2_stream_id , nva , 2 , NULL );
561+ nghttp2_session_send (session );
562+ }
535563 }
536564
537565 /* Timer already fired, clear it before destroy */
@@ -553,7 +581,6 @@ static int dns_send_request_h2(struct req_slot *req)
553581 ret = sendto (req -> dns_sd , req -> h2_request_buffer , req -> h2_request_len , 0 ,
554582 (struct sockaddr * )req -> resolver , req -> resolver_sz );
555583 if (ret < 0 ) {
556- dohprint (DOH_ERR , "Resolver send failed: %s\n" , strerror (errno ));
557584 DOH_Stats .socket_errors ++ ;
558585 check_stats ();
559586 dohd_destroy_request (req );
@@ -572,17 +599,6 @@ static int dns_send_request_h2(struct req_slot *req)
572599
573600static ssize_t client_ssl_write (struct client_data * cd , const void * data , size_t len )
574601{
575- #ifdef VERBOSE_HTTP_DEBUG
576- int i ;
577- uint8_t * reply = (uint8_t * )data ;
578- dohprint (DOH_NOTICE ,"\n\nSSL Write: %lu bytes\n" , len );
579- for (i = 0 ; i < len ; i ++ ) {
580- dohprint (DOH_NOTICE ,"%02x " , (reply [i ]));
581- if ((i % 16 ) == 15 )
582- dohprint (DOH_NOTICE ,"\n" );
583- }
584- dohprint (DOH_NOTICE ,"\n\n" );
585- #endif
586602 return wolfSSL_write (cd -> ssl , data , len );
587603}
588604
@@ -677,7 +693,6 @@ static uint32_t dnsreply_min_age(const void *p, size_t len)
677693 for (i = 0 ; i < ntohs (hdr -> qdcount ); i ++ ) {
678694 skip = dns_skip_question (& record , len );
679695 if (skip < DNSQ_SUFFIX_LEN ) {
680- dohprint (DOH_WARN , "Cannot parse DNS reply!\n" );
681696 return min_ttl ;
682697 }
683698 len -= skip ;
@@ -819,7 +834,6 @@ static void dohd_reply(int fd, short __attribute__((unused)) revents,
819834
820835 len = recv (fd , buff , bufsz , 0 );
821836 if (len < 16 ) {
822- dohprint (DOH_ERR , "Error while receiving DNS reply: socket error %d\n" , errno );
823837 goto destroy ;
824838 }
825839
@@ -855,7 +869,6 @@ static void dohd_reply(int fd, short __attribute__((unused)) revents,
855869 data_prd .read_callback = h2_cb_req_submit ;
856870 if (nghttp2_submit_response (req -> owner -> h2_session ,
857871 req -> h2_stream_id , nva , 4 , & data_prd ) < 0 ) {
858- dohprint (DOH_WARN , "H2: submit response failed\n" );
859872 dohd_destroy_request (req );
860873 return ;
861874 }
@@ -900,15 +913,12 @@ static int h2_cb_on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
900913 }
901914 req = nghttp2_session_get_stream_user_data (session , stream_id );
902915 if (!req ) {
903- dohprint (DOH_WARN , "H2: received bogus DATA not associated to request\n" );
904916 goto data_fail ;
905917 }
906918 if (req -> owner != cd ) {
907- dohprint (DOH_WARN , "H2: received DATA chunk with wrong stream_id\n" );
908919 goto data_fail ;
909920 }
910921 if (len > DNS_BUFFER_MAXSIZE ) {
911- dohprint (DOH_WARN , "H2: received DATA chunk too large (%lu)\n" , len );
912922 goto data_fail ;
913923 }
914924 memcpy (req -> h2_request_buffer , data , len );
@@ -1003,6 +1013,14 @@ static int h2_cb_on_header(nghttp2_session *session,
10031013 if (!req ) {
10041014 req = dns_create_request_h2 (cd , frame -> hd .stream_id );
10051015 if (!req ) {
1016+ /* Pool exhausted or socket creation failed - send 503 */
1017+ nghttp2_nv nva [] = {
1018+ MAKE_NV (":status" , "503" ),
1019+ MAKE_NV ("server" , "dohd" ),
1020+ MAKE_NV ("retry-after" , "1" ),
1021+ };
1022+ nghttp2_submit_response (session , frame -> hd .stream_id , nva , 3 , NULL );
1023+ DOH_Stats .socket_errors ++ ;
10061024 return 0 ;
10071025 }
10081026 }
0 commit comments