66#include <stdio.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
77#include <string.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
88#include <sys/socket.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
9- #include <unistd.h>
9+ #include <unistd.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
1010
1111#include "https_client.h"
1212#include "logging.h"
1313#include "options.h"
1414
1515#define DOH_CONTENT_TYPE "application/dns-message"
16+ #define DOH_MAX_RESPONSE_SIZE 65535
1617
1718// the following macros require to have ctx pointer to https_fetch_ctx structure
1819// else: compilation failure will occur
4748
4849static size_t write_buffer (void * buf , size_t size , size_t nmemb , void * userp ) {
4950 GET_PTR (struct https_fetch_ctx , ctx , userp );
50- char * new_buf = (char * )realloc (
51- ctx -> buf , ctx -> buflen + size * nmemb + 1 );
51+ size_t write_size = size * nmemb ;
52+ size_t new_size = ctx -> buflen + write_size ;
53+ if (new_size > DOH_MAX_RESPONSE_SIZE ) {
54+ WLOG_REQ ("Response size is too large!" );
55+ return 0 ;
56+ }
57+ char * new_buf = (char * )realloc (ctx -> buf , new_size + 1 );
5258 if (new_buf == NULL ) {
5359 ELOG_REQ ("Out of memory!" );
5460 return 0 ;
5561 }
5662 ctx -> buf = new_buf ;
5763 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
58- memcpy (& (ctx -> buf [ctx -> buflen ]), buf , size * nmemb );
59- ctx -> buflen += size * nmemb ;
64+ memcpy (& (ctx -> buf [ctx -> buflen ]), buf , write_size );
65+ ctx -> buflen = new_size ;
6066 // We always expect to receive valid non-null ASCII but just to be safe...
6167 ctx -> buf [ctx -> buflen ] = '\0' ;
62- return size * nmemb ;
68+ return write_size ;
6369}
6470
6571static curl_socket_t opensocket_callback (void * clientp , curlsocktype purpose ,
6672 struct curl_sockaddr * addr ) {
6773 GET_PTR (https_client_t , client , clientp );
6874
6975 curl_socket_t sock = socket (addr -> family , addr -> socktype , addr -> protocol );
70-
71- DLOG ("curl opened socket: %d" , sock );
76+ if (sock != -1 ) {
77+ DLOG ("curl opened socket: %d" , sock );
78+ } else {
79+ ELOG ("Could not open curl socket %d:%s" , errno , strerror (errno ));
80+ return CURL_SOCKET_BAD ;
81+ }
7282
7383 if (client -> stat ) {
7484 stat_connection_opened (client -> stat );
@@ -79,18 +89,16 @@ static curl_socket_t opensocket_callback(void *clientp, curlsocktype purpose,
7989 return sock ;
8090 }
8191
82- if (sock != -1 ) {
83- if (addr -> family == AF_INET ) {
84- setsockopt (sock , IPPROTO_IP , IP_TOS ,
85- & client -> opt -> dscp , sizeof (client -> opt -> dscp ));
86- }
92+ if (addr -> family == AF_INET ) {
93+ setsockopt (sock , IPPROTO_IP , IP_TOS ,
94+ & client -> opt -> dscp , sizeof (client -> opt -> dscp ));
95+ }
8796#if defined(IPV6_TCLASS )
88- else if (addr -> family == AF_INET6 ) {
89- setsockopt (sock , IPPROTO_IPV6 , IPV6_TCLASS ,
90- & client -> opt -> dscp , sizeof (client -> opt -> dscp ));
91- }
92- #endif
97+ else if (addr -> family == AF_INET6 ) {
98+ setsockopt (sock , IPPROTO_IPV6 , IPV6_TCLASS ,
99+ & client -> opt -> dscp , sizeof (client -> opt -> dscp ));
93100 }
101+ #endif
94102#endif
95103
96104 return sock ;
@@ -103,7 +111,8 @@ static int closesocket_callback(void __attribute__((unused)) *clientp, curl_sock
103111 if (close (sock ) == 0 ) {
104112 DLOG ("curl closed socket: %d" , sock );
105113 } else {
106- FLOG ("Could not close curl socket %d:%s" , errno , strerror (errno ));
114+ ELOG ("Could not close curl socket %d:%s" , errno , strerror (errno ));
115+ return 1 ;
107116 }
108117
109118 if (client -> stat ) {
@@ -225,6 +234,7 @@ static void https_fetch_ctx_init(https_client_t *client,
225234 CURL_HTTP_VERSION_1_1 :
226235 CURL_HTTP_VERSION_2_0 );
227236 if (easy_code != CURLE_OK ) {
237+ // hopefully errors will be logged once
228238 ELOG_REQ ("CURLOPT_HTTP_VERSION error %d: %s" ,
229239 easy_code , curl_easy_strerror (easy_code ));
230240 if (!client -> opt -> use_http_1_1 ) {
@@ -264,6 +274,7 @@ static void https_fetch_ctx_init(https_client_t *client,
264274 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_TIMEOUT , 10 /* seconds */ );
265275 // We know Google supports this, so force it.
266276 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_SSLVERSION , CURL_SSLVERSION_TLSv1_2 );
277+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_ERRORBUFFER , ctx -> curl_errbuf ); // zeroed by calloc
267278 if (client -> opt -> curl_proxy ) {
268279 DLOG_REQ ("Using curl proxy: %s" , client -> opt -> curl_proxy );
269280 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_PROXY , client -> opt -> curl_proxy );
@@ -276,40 +287,57 @@ static void https_fetch_ctx_init(https_client_t *client,
276287}
277288
278289static int https_fetch_ctx_process_response (https_client_t * client ,
279- struct https_fetch_ctx * ctx )
290+ struct https_fetch_ctx * ctx ,
291+ int curl_result_code )
280292{
281293 CURLcode res = 0 ;
282294 long long_resp = 0 ;
283295 char * str_resp = NULL ;
284296 int faulty_response = 1 ;
285297
298+ switch (curl_result_code ) {
299+ case CURLE_OK :
300+ DLOG_REQ ("curl request succeeded" );
301+ faulty_response = 0 ;
302+ break ;
303+ case CURLE_WRITE_ERROR :
304+ WLOG_REQ ("Response content was too large" );
305+ break ;
306+ default :
307+ WLOG_REQ ("curl request failed with %d: %s" , res , curl_easy_strerror (res ));
308+ if (ctx -> curl_errbuf [0 ] != 0 ) {
309+ WLOG_REQ ("curl error message: %s" , ctx -> curl_errbuf );
310+ }
311+ }
312+
286313 if ((res = curl_easy_getinfo (
287314 ctx -> curl , CURLINFO_RESPONSE_CODE , & long_resp )) != CURLE_OK ) {
288315 ELOG_REQ ("CURLINFO_RESPONSE_CODE: %s" , curl_easy_strerror (res ));
289- } else {
290- if (long_resp = = 200 ) {
291- faulty_response = 0 ;
292- } else if (long_resp == 0 ) {
316+ faulty_response = 1 ;
317+ } else if (long_resp ! = 200 ) {
318+ faulty_response = 1 ;
319+ if (long_resp == 0 ) {
293320 curl_off_t uploaded_bytes = 0 ;
294321 if (curl_easy_getinfo (ctx -> curl , CURLINFO_SIZE_UPLOAD_T , & uploaded_bytes ) == CURLE_OK &&
295322 uploaded_bytes > 0 ) {
296- ELOG_REQ ("Connecting and sending request to resolver was successful, "
323+ WLOG_REQ ("Connecting and sending request to resolver was successful, "
297324 "but no response was sent back" );
298325 if (client -> opt -> use_http_1_1 ) {
299326 // for example Unbound DoH servers does not support HTTP/1.x, only HTTP/2
300- ELOG ("Resolver may not support current HTTP/1.1 protocol version" );
327+ WLOG ("Resolver may not support current HTTP/1.1 protocol version" );
301328 }
302329 } else {
303330 // in case of HTTP/1.1 this can happen very often depending on DNS query frequency
304331 // example: server side closes the connection or curl force closes connections
305332 // that have been opened a long time ago (if CURLOPT_MAXAGE_CONN can not be increased
306333 // it is 118 seconds)
334+ // also: when no internet connection, this floods the log for every failed request
307335 WLOG_REQ ("No response (probably connection has been closed or timed out)" );
308336 }
309337 } else {
310- ELOG_REQ ("curl response code: %d, content length: %zu" , long_resp , ctx -> buflen );
338+ WLOG_REQ ("curl response code: %d, content length: %zu" , long_resp , ctx -> buflen );
311339 if (ctx -> buflen > 0 ) {
312- https_log_data (LOG_ERROR , ctx , ctx -> buf , ctx -> buflen );
340+ https_log_data (LOG_WARNING , ctx , ctx -> buf , ctx -> buflen );
313341 }
314342 }
315343 }
@@ -319,12 +347,10 @@ static int https_fetch_ctx_process_response(https_client_t *client,
319347 if ((res = curl_easy_getinfo (
320348 ctx -> curl , CURLINFO_CONTENT_TYPE , & str_resp )) != CURLE_OK ) {
321349 ELOG_REQ ("CURLINFO_CONTENT_TYPE: %s" , curl_easy_strerror (res ));
322- } else {
323- if (str_resp == NULL ||
324- strncmp (str_resp , DOH_CONTENT_TYPE , sizeof (DOH_CONTENT_TYPE ) - 1 ) != 0 ) { // at least, start with it
325- ELOG_REQ ("Invalid response Content-Type: %s" , str_resp ? str_resp : "UNSET" );
326- faulty_response = 1 ;
327- }
350+ } else if (str_resp == NULL ||
351+ strncmp (str_resp , DOH_CONTENT_TYPE , sizeof (DOH_CONTENT_TYPE ) - 1 ) != 0 ) { // at least, start with it
352+ WLOG_REQ ("Invalid response Content-Type: %s" , str_resp ? str_resp : "UNSET" );
353+ faulty_response = 1 ;
328354 }
329355 }
330356
@@ -333,24 +359,25 @@ static int https_fetch_ctx_process_response(https_client_t *client,
333359 ctx -> curl , CURLINFO_REDIRECT_URL , & str_resp )) != CURLE_OK ) {
334360 ELOG_REQ ("CURLINFO_REDIRECT_URL: %s" , curl_easy_strerror (res ));
335361 } else if (str_resp != NULL ) {
336- ELOG_REQ ("Request would be redirected to: %s" , str_resp );
362+ WLOG_REQ ("Request would be redirected to: %s" , str_resp );
337363 if (strcmp (str_resp , client -> opt -> resolver_url ) != 0 ) {
338- ELOG ("Please update Resolver URL to avoid redirection!" );
364+ WLOG ("Please update Resolver URL to avoid redirection!" );
339365 }
340366 }
341367 if ((res = curl_easy_getinfo (
342368 ctx -> curl , CURLINFO_SSL_VERIFYRESULT , & long_resp )) != CURLE_OK ) {
343369 ELOG_REQ ("CURLINFO_SSL_VERIFYRESULT: %s" , curl_easy_strerror (res ));
344370 } else if (long_resp != CURLE_OK ) {
345- ELOG_REQ ("CURLINFO_SSL_VERIFYRESULT: %s" , curl_easy_strerror (long_resp ));
371+ WLOG_REQ ("CURLINFO_SSL_VERIFYRESULT: %s" , curl_easy_strerror (long_resp ));
346372 }
347373 if ((res = curl_easy_getinfo (
348374 ctx -> curl , CURLINFO_OS_ERRNO , & long_resp )) != CURLE_OK ) {
349375 ELOG_REQ ("CURLINFO_OS_ERRNO: %s" , curl_easy_strerror (res ));
350376 } else if (long_resp != 0 ) {
351- ELOG_REQ ("CURLINFO_OS_ERRNO: %d %s" , long_resp , strerror (long_resp ));
377+ WLOG_REQ ("CURLINFO_OS_ERRNO: %d %s" , long_resp , strerror (long_resp ));
352378 if (long_resp == ENETUNREACH && !client -> opt -> ipv4 ) {
353- ELOG ("Try to run application with -4 argument!" );
379+ // this can't be fixed here with option overwrite because of dns_poller
380+ WLOG ("Try to run application with -4 argument!" );
354381 }
355382 }
356383 }
@@ -429,7 +456,8 @@ static int https_fetch_ctx_process_response(https_client_t *client,
429456}
430457
431458static void https_fetch_ctx_cleanup (https_client_t * client ,
432- struct https_fetch_ctx * ctx ) {
459+ struct https_fetch_ctx * ctx ,
460+ int curl_result_code ) {
433461 struct https_fetch_ctx * last = NULL ;
434462 struct https_fetch_ctx * cur = client -> fetches ;
435463 while (cur ) {
@@ -438,8 +466,15 @@ static void https_fetch_ctx_cleanup(https_client_t *client,
438466 if (code != CURLM_OK ) {
439467 FLOG_REQ ("curl_multi_remove_handle error %d: %s" , code , curl_multi_strerror (code ));
440468 }
441- if (https_fetch_ctx_process_response (client , ctx ) != 0 ) {
469+ int drop_reply = 0 ;
470+ if (curl_result_code < 0 ) {
471+ WLOG_REQ ("Request was aborted." );
472+ drop_reply = 1 ;
473+ } else if (https_fetch_ctx_process_response (client , ctx , curl_result_code ) != 0 ) {
442474 ILOG_REQ ("Response was faulty, skipping DNS reply." );
475+ drop_reply = 1 ;
476+ }
477+ if (drop_reply ) {
443478 free (ctx -> buf );
444479 ctx -> buf = NULL ;
445480 ctx -> buflen = 0 ;
@@ -469,7 +504,7 @@ static void check_multi_info(https_client_t *c) {
469504 struct https_fetch_ctx * n = c -> fetches ;
470505 while (n ) {
471506 if (n -> curl == msg -> easy_handle ) {
472- https_fetch_ctx_cleanup (c , n );
507+ https_fetch_ctx_cleanup (c , n , msg -> data . result );
473508 break ;
474509 }
475510 n = n -> next ;
@@ -570,10 +605,7 @@ void https_client_init(https_client_t *c, options_t *opt,
570605 c -> opt = opt ;
571606 c -> stat = stat ;
572607
573- ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_PIPELINING ,
574- c -> opt -> use_http_1_1 ?
575- CURLPIPE_HTTP1 :
576- CURLPIPE_MULTIPLEX );
608+ ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_PIPELINING , CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX );
577609 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_MAX_TOTAL_CONNECTIONS , MAX_TOTAL_CONNECTIONS );
578610 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_SOCKETDATA , c );
579611 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_SOCKETFUNCTION , multi_sock_cb );
@@ -603,7 +635,7 @@ void https_client_reset(https_client_t *c) {
603635
604636void https_client_cleanup (https_client_t * c ) {
605637 while (c -> fetches ) {
606- https_fetch_ctx_cleanup (c , c -> fetches );
638+ https_fetch_ctx_cleanup (c , c -> fetches , -1 );
607639 }
608640 curl_slist_free_all (c -> header_list );
609641 curl_multi_cleanup (c -> curlm );
0 commit comments