1- #include <ares.h>
21#include <arpa/inet.h>
32#include <errno.h>
43#include <stdint.h>
98
109#include "dns_common.h"
1110#include "dns_listener_udp.h"
11+ #include "dns_truncate.h"
1212#include "logging.h"
1313
1414typedef struct dns_listener_udp_s {
@@ -86,116 +86,6 @@ static void watcher_cb(struct ev_loop __attribute__((unused)) *loop,
8686 d -> cb (d -> cb_data , & d -> base , (struct sockaddr * )& tmp_raddr , dns_req , (size_t )len );
8787}
8888
89- static uint16_t get_edns_udp_size (const char * dns_req , const size_t dns_req_len ) {
90- ares_dns_record_t * dnsrec = NULL ;
91- ares_status_t parse_status = ares_dns_parse ((const unsigned char * )dns_req , dns_req_len , 0 , & dnsrec );
92- if (parse_status != ARES_SUCCESS ) {
93- WLOG ("Failed to parse DNS request: %s" , ares_strerror ((int )parse_status ));
94- return DNS_SIZE_LIMIT ;
95- }
96- const uint16_t tx_id = ares_dns_record_get_id (dnsrec );
97- uint16_t udp_size = 0 ;
98- const size_t record_count = ares_dns_record_rr_cnt (dnsrec , ARES_SECTION_ADDITIONAL );
99- for (size_t i = 0 ; i < record_count ; ++ i ) {
100- const ares_dns_rr_t * rr = ares_dns_record_rr_get (dnsrec , ARES_SECTION_ADDITIONAL , i );
101- if (ares_dns_rr_get_type (rr ) == ARES_REC_TYPE_OPT ) {
102- udp_size = ares_dns_rr_get_u16 (rr , ARES_RR_OPT_UDP_SIZE );
103- if (udp_size > 0 ) {
104- DLOG ("%04hX: Found EDNS0 UDP buffer size: %u" , tx_id , udp_size );
105- }
106- break ;
107- }
108- }
109- ares_dns_record_destroy (dnsrec );
110- if (udp_size < DNS_SIZE_LIMIT ) {
111- DLOG ("%04hX: EDNS0 UDP buffer size %u overruled to %d" , tx_id , udp_size , DNS_SIZE_LIMIT );
112- return DNS_SIZE_LIMIT ; // RFC6891 4.3 "Values lower than 512 MUST be treated as equal to 512."
113- }
114- return udp_size ;
115- }
116-
117- static void truncate_dns_response (char * buf , size_t * buflen , const uint16_t size_limit ) {
118- const size_t old_size = * buflen ;
119- buf [2 ] |= 0x02 ; // anyway: set truncation flag
120-
121- ares_dns_record_t * dnsrec = NULL ;
122- ares_status_t status = ares_dns_parse ((const unsigned char * )buf , * buflen , 0 , & dnsrec );
123- if (status != ARES_SUCCESS ) {
124- WLOG ("Failed to parse DNS response: %s" , ares_strerror ((int )status ));
125- return ;
126- }
127- const uint16_t tx_id = ares_dns_record_get_id (dnsrec );
128-
129- // NOTE: according to current c-ares implementation, removing first or last elements are the fastest!
130-
131- // remove every additional and authority record
132- while (ares_dns_record_rr_cnt (dnsrec , ARES_SECTION_ADDITIONAL ) > 0 ) {
133- status = ares_dns_record_rr_del (dnsrec , ARES_SECTION_ADDITIONAL , 0 );
134- if (status != ARES_SUCCESS ) {
135- WLOG ("%04hX: Could not remove additional record: %s" , tx_id , ares_strerror ((int )status ));
136- }
137- }
138- while (ares_dns_record_rr_cnt (dnsrec , ARES_SECTION_AUTHORITY ) > 0 ) {
139- status = ares_dns_record_rr_del (dnsrec , ARES_SECTION_AUTHORITY , 0 );
140- if (status != ARES_SUCCESS ) {
141- WLOG ("%04hX: Could not remove authority record: %s" , tx_id , ares_strerror ((int )status ));
142- }
143- }
144-
145- // rough estimate to reach size limit
146- size_t answers = ares_dns_record_rr_cnt (dnsrec , ARES_SECTION_ANSWER );
147- size_t answers_to_keep = ((size_limit - DNS_HEADER_LENGTH ) * answers ) / old_size ;
148- answers_to_keep = answers_to_keep > 0 ? answers_to_keep : 1 ; // try to keep 1 answer
149-
150- // remove answer records until fit size limit or running out of answers
151- unsigned char * new_resp = NULL ;
152- size_t new_resp_len = 0 ;
153- for (uint8_t g = 0 ; g < UINT8_MAX ; ++ g ) { // endless loop guard
154- status = ares_dns_write (dnsrec , & new_resp , & new_resp_len );
155- if (status != ARES_SUCCESS ) {
156- WLOG ("%04hX: Failed to create truncated DNS response: %s" , tx_id , ares_strerror ((int )status ));
157- new_resp = NULL ; // just to be sure
158- break ;
159- }
160- if (new_resp_len < size_limit || answers == 0 ) {
161- break ;
162- }
163- if (new_resp_len >= old_size ) {
164- WLOG ("%04hX: Truncated DNS response size larger or equal to original: %u >= %u" ,
165- tx_id , new_resp_len , old_size ); // impossible?
166- }
167- ares_free_string (new_resp );
168- new_resp = NULL ;
169-
170- DLOG ("%04hX: DNS response size truncated from %u to %u but to keep %u limit reducing answers from %u to %u" ,
171- tx_id , old_size , new_resp_len , size_limit , answers , answers_to_keep );
172-
173- while (answers > answers_to_keep ) {
174- status = ares_dns_record_rr_del (dnsrec , ARES_SECTION_ANSWER , answers - 1 );
175- if (status != ARES_SUCCESS ) {
176- WLOG ("%04hX: Could not remove answer record: %s" , tx_id , ares_strerror ((int )status ));
177- break ;
178- }
179- -- answers ;
180- }
181- answers = ares_dns_record_rr_cnt (dnsrec , ARES_SECTION_ANSWER ); // update to be sure!
182- answers_to_keep /= 2 ;
183- }
184- ares_dns_record_destroy (dnsrec );
185-
186- if (new_resp != NULL ) {
187- if (new_resp_len < old_size ) {
188- memcpy (buf , new_resp , new_resp_len );
189- * buflen = new_resp_len ;
190- buf [2 ] |= 0x02 ; // set truncation flag
191- ILOG ("%04hX: DNS response size truncated from %u to %u to keep %u limit" ,
192- tx_id , old_size , new_resp_len , size_limit );
193- }
194- ares_free_string (new_resp );
195- new_resp = NULL ;
196- }
197- }
198-
19989static void udp_respond (dns_listener_t * self , struct sockaddr * raddr ,
20090 const char * dns_req , size_t dns_req_len ,
20191 char * dns_resp , size_t dns_resp_len ) {
@@ -205,16 +95,7 @@ static void udp_respond(dns_listener_t *self, struct sockaddr *raddr,
20595 WLOG ("Malformed response received, invalid length: %u" , dns_resp_len );
20696 return ;
20797 }
208- if (dns_resp_len > DNS_SIZE_LIMIT ) {
209- const uint16_t udp_size = get_edns_udp_size (dns_req , dns_req_len );
210- if (dns_resp_len > udp_size ) {
211- truncate_dns_response (dns_resp , & dns_resp_len , udp_size );
212- } else {
213- uint16_t tx_id = ntohs (* ((uint16_t * )dns_req ));
214- DLOG ("%04hX: DNS response size %u larger than %d but EDNS0 UDP buffer size %u allows it" ,
215- tx_id , dns_resp_len , DNS_SIZE_LIMIT , udp_size );
216- }
217- }
98+ dns_truncate_for_udp (dns_req , dns_req_len , dns_resp , & dns_resp_len );
21899
219100 ssize_t len = sendto (d -> sock , dns_resp , dns_resp_len , 0 , raddr , d -> addrlen );
220101 if (len == -1 ) {
0 commit comments