1- //NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
2- #define _GNU_SOURCE // needed for having accept4()
3-
41#include <arpa/inet.h>
52#include <errno.h>
63#include <fcntl.h>
3431enum {
3532 LISTEN_BACKLOG = 5 ,
3633 IDLE_TIMEOUT_S = 120 , // "two minutes" according to RFC1035 4.2.2
37- RESEND_DELAY_US = 500 , // 0.0005 sec
34+ RESPONSE_SEND_ATTEMPTS = 50 , // 0.025 sec max wait
35+ RESPONSE_SEND_DELAY_US = 500 , // 0.0005 sec
3836 TCP_DNS_MAX_PAYLOAD = UINT16_MAX - sizeof (uint16_t ), // Max after 2-byte length prefix
3937};
4038
@@ -95,17 +93,13 @@ static void remove_client(struct tcp_client_s * client) {
9593
9694 close (client -> sock );
9795
98- // Save next pointer before freeing. Safe because this is single-threaded
99- // event loop - no callbacks can run during this function.
100- struct tcp_client_s * next = client -> next ;
101-
10296 if (d -> clients == client ) {
103- d -> clients = next ;
97+ d -> clients = client -> next ;
10498 }
10599 else {
106100 for (struct tcp_client_s * cur = d -> clients ; cur != NULL ; cur = cur -> next ) {
107101 if (cur -> next == client ) {
108- cur -> next = next ;
102+ cur -> next = client -> next ;
109103 break ;
110104 }
111105 }
@@ -144,11 +138,11 @@ static void read_cb(struct ev_loop __attribute__((unused)) *loop,
144138 ssize_t len = recv (w -> fd , buf , DNS_REQUEST_BUFFER_SIZE , 0 );
145139 if (len <= 0 ) {
146140 if (len == 0 || errno == ECONNRESET ) {
147- DLOG_CLIENT ("Connection closed" );
141+ DLOG_CLIENT ("TCP client closed connection " );
148142 } else if (errno == EAGAIN || errno == EWOULDBLOCK ) {
149143 return ;
150144 } else {
151- WLOG_CLIENT ("Read error: %s" , strerror (errno ));
145+ WLOG_CLIENT ("Read error: %s (%d), dropping client " , strerror (errno ), errno );
152146 }
153147 remove_client (client );
154148 return ;
@@ -194,12 +188,13 @@ static void read_cb(struct ev_loop __attribute__((unused)) *loop,
194188 uint8_t request_received = 0 ;
195189 while (get_dns_request (client , & dns_req , & req_size )) {
196190 if (req_size < DNS_HEADER_LENGTH ) {
197- WLOG_CLIENT ("Malformed request received, too short: %u" , req_size );
191+ WLOG_CLIENT ("Malformed request received, too short: %u, dropping client " , req_size );
198192 free (dns_req );
199193 remove_client (client );
200194 return ;
201195 }
202196
197+ DLOG_CLIENT ("Requested %04hX" , ntohs (* ((uint16_t * )dns_req )));
203198 d -> cb (d -> cb_data , & d -> base , (struct sockaddr * )& client -> raddr , dns_req , req_size );
204199 request_received = 1 ;
205200 }
@@ -223,16 +218,27 @@ static void accept_cb(struct ev_loop __attribute__((unused)) *loop,
223218 struct sockaddr_storage client_addr ;
224219 socklen_t client_addr_len = sizeof (client_addr );
225220
226- int client_sock = accept (w -> fd , (struct sockaddr * )& client_addr , & client_addr_len );
227- if (client_sock != -1 ) {
228- // Set non-blocking mode for macOS compatibility (Linux accept4 does this atomically)
229- int flags = fcntl (client_sock , F_GETFL , 0 );
230- if (flags != -1 ) {
231- fcntl (client_sock , F_SETFL , flags | O_NONBLOCK );
221+ // NOLINTNEXTLINE(android-cloexec-accept)
222+ const int client_sock = accept (w -> fd , (struct sockaddr * )& client_addr , & client_addr_len );
223+ if (client_sock == -1 ) {
224+ if (errno != EAGAIN && errno != EWOULDBLOCK ) {
225+ ELOG ("Failed to accept TCP client: %s (%d)" , strerror (errno ), errno );
232226 }
227+ return ;
233228 }
234- if (client_sock == -1 && errno != EAGAIN && errno != EWOULDBLOCK ) {
235- ELOG ("Failed to accept TCP client: %s" , strerror (errno ));
229+
230+ // Set non-blocking mode for macOS compatibility (Linux accept4 does this atomically)
231+ const int flags = fcntl (client_sock , F_GETFL , 0 );
232+ if (flags == -1 ) {
233+ ELOG ("Error getting TCP client socket flags: %s (%d), dropping client" ,
234+ strerror (errno ), errno );
235+ close (client_sock );
236+ return ;
237+ }
238+ if (fcntl (client_sock , F_SETFL , flags | O_NONBLOCK ) == -1 ) {
239+ ELOG ("Error setting TCP client socket to non-blocking: %s (%d), dropping client" ,
240+ strerror (errno ), errno );
241+ close (client_sock );
236242 return ;
237243 }
238244
@@ -329,6 +335,7 @@ static void tcp_respond(dns_listener_t *self, struct sockaddr *raddr,
329335 WLOG ("Malformed response received, invalid length: %u" , resp_len );
330336 return ;
331337 }
338+ const uint16_t response_id = ntohs (* ((uint16_t * )resp ));
332339
333340 // find client data
334341 struct tcp_client_s * client = NULL ;
@@ -339,7 +346,6 @@ static void tcp_respond(dns_listener_t *self, struct sockaddr *raddr,
339346 }
340347 }
341348 if (client == NULL ) {
342- uint16_t response_id = ntohs (* ((uint16_t * )resp ));
343349 WLOG ("Could not find client, can not send DNS response: %04hX" , response_id );
344350 return ;
345351 }
@@ -355,37 +361,38 @@ static void tcp_respond(dns_listener_t *self, struct sockaddr *raddr,
355361 uint16_t resp_size = htons ((uint16_t )resp_len );
356362 ssize_t len = send (client -> sock , & resp_size , sizeof (uint16_t ), MSG_MORE | MSG_NOSIGNAL );
357363 if (len != sizeof (uint16_t )) {
358- WLOG_CLIENT ("Send error: %s, len: %d" , strerror (errno ), len );
364+ WLOG_CLIENT ("Send error: %s (%d) , len: %d, dropping client " , strerror (errno ), errno , len );
359365 remove_client (client );
360366 return ;
361367 }
362368
363369 // send the response
364370 ssize_t sent = 0 ;
365371 int attempts = 0 ;
366- for (; attempts < 50 ; ++ attempts ) // 25ms max wait
372+ for (; attempts < RESPONSE_SEND_ATTEMPTS ; ++ attempts )
367373 {
368374 len = send (client -> sock , resp + sent , resp_len - (size_t )sent , MSG_NOSIGNAL );
369- if (len < 0 ) {
375+ if (len > 0 ) {
376+ sent += len ;
377+ if (sent == (ssize_t )resp_len ) {
378+ break ;
379+ }
380+ } else if (len < 0 ) {
370381 if (errno != EAGAIN && errno != EWOULDBLOCK ) {
371- WLOG_CLIENT ("Send error: %s" , strerror (errno ));
382+ WLOG_CLIENT ("Send error: %s (%d), dropping client " , strerror (errno ), errno );
372383 remove_client (client );
373384 return ;
374385 }
375- // EAGAIN/EWOULDBLOCK - socket buffer full, retry after delay
376- continue ;
377- }
378- sent += len ;
379- if (sent == (ssize_t )resp_len ) {
380- break ;
381386 }
382- usleep (RESEND_DELAY_US );
387+ usleep (RESPONSE_SEND_DELAY_US );
383388 }
384389 if (sent != (ssize_t )resp_len ) {
385- WLOG_CLIENT ("Send timeout after %d attempts, sent %zd/%zu bytes" , attempts , sent , resp_len );
390+ WLOG_CLIENT ("Send timeout after %d attempts, sent %zd/%zu bytes, dropping client" ,
391+ attempts , sent , resp_len );
386392 remove_client (client );
387393 return ;
388394 }
395+ DLOG_CLIENT ("Responded %04hX" , response_id );
389396
390397 ev_timer_again (d -> loop , & client -> timer_watcher );
391398}
0 commit comments