@@ -50,6 +50,11 @@ DOH_MAX_RESPONSE_SIZE = 65535
5050 FLOG("Unexpected NULL pointer for " #var_name "(" #type ")"); \
5151 }
5252
53+ static void https_fetch_ctx_cleanup (https_client_t * client ,
54+ struct https_fetch_ctx * prev ,
55+ struct https_fetch_ctx * ctx ,
56+ int curl_result_code );
57+
5358static size_t write_buffer (void * buf , size_t size , size_t nmemb , void * userp ) {
5459 GET_PTR (struct https_fetch_ctx , ctx , userp );
5560 size_t write_size = size * nmemb ;
@@ -76,14 +81,20 @@ static curl_socket_t opensocket_callback(void *clientp, curlsocktype purpose,
7681 struct curl_sockaddr * addr ) {
7782 GET_PTR (https_client_t , client , clientp );
7883
84+ if (client -> connections >= HTTPS_SOCKET_LIMIT ) {
85+ ELOG ("curl needed more socket, than the number of maximum sockets: %d" , HTTPS_SOCKET_LIMIT );
86+ return CURL_SOCKET_BAD ;
87+ }
88+
7989 curl_socket_t sock = socket (addr -> family , addr -> socktype , addr -> protocol );
80- if (sock != -1 ) {
81- DLOG ("curl opened socket: %d" , sock );
82- } else {
90+ if (sock == -1 ) {
8391 ELOG ("Could not open curl socket %d:%s" , errno , strerror (errno ));
8492 return CURL_SOCKET_BAD ;
8593 }
8694
95+ DLOG ("curl opened socket: %d" , sock );
96+ client -> connections ++ ;
97+
8798 if (client -> stat ) {
8899 stat_connection_opened (client -> stat );
89100 }
@@ -112,13 +123,14 @@ static int closesocket_callback(void __attribute__((unused)) *clientp, curl_sock
112123{
113124 GET_PTR (https_client_t , client , clientp );
114125
115- if (close (sock ) == 0 ) {
116- DLOG ("curl closed socket: %d" , sock );
117- } else {
126+ if (close (sock ) != 0 ) {
118127 ELOG ("Could not close curl socket %d:%s" , errno , strerror (errno ));
119128 return 1 ;
120129 }
121130
131+ DLOG ("curl closed socket: %d" , sock );
132+ client -> connections -- ;
133+
122134 if (client -> stat ) {
123135 stat_connection_closed (client -> stat );
124136 }
@@ -265,7 +277,6 @@ static void https_set_request_version(https_client_t *client,
265277 } else if (client -> opt -> use_http_version == 2 ) {
266278 ELOG ("Try to run application with -x argument! Falling back to HTTP/1.1 version." );
267279 client -> opt -> use_http_version = 1 ;
268- // TODO: consider CURLMOPT_PIPELINING setting??
269280 }
270281 }
271282}
@@ -293,30 +304,22 @@ static void https_fetch_ctx_init(https_client_t *client,
293304 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_DEBUGFUNCTION , https_curl_debug );
294305 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_DEBUGDATA , ctx );
295306 }
296- if (logging_debug_enabled () || client -> stat || client -> opt -> dscp ) {
297- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_OPENSOCKETFUNCTION , opensocket_callback );
298- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_OPENSOCKETDATA , client );
299- }
300- if (logging_debug_enabled () || client -> stat ) {
301- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_CLOSESOCKETFUNCTION , closesocket_callback );
302- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_CLOSESOCKETDATA , client );
303- }
307+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_OPENSOCKETFUNCTION , opensocket_callback );
308+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_OPENSOCKETDATA , client );
309+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_CLOSESOCKETFUNCTION , closesocket_callback );
310+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_CLOSESOCKETDATA , client );
304311 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_URL , url );
305312 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_HTTPHEADER , client -> header_list );
306313 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_POSTFIELDSIZE , datalen );
307314 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_POSTFIELDS , data );
308315 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_WRITEFUNCTION , & write_buffer );
309316 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_WRITEDATA , ctx );
310- #ifdef CURLOPT_MAXAGE_CONN
311- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_TCP_KEEPALIVE , 1L );
312- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_TCP_KEEPIDLE , 50L );
313- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_TCP_KEEPINTVL , 50L );
314- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_MAXAGE_CONN , 300L );
315- #endif
317+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_MAXAGE_CONN , client -> opt -> max_idle_time );
318+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_PIPEWAIT , client -> opt -> use_http_version > 1 );
316319 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_USERAGENT , "https_dns_proxy/0.3" );
317320 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_FOLLOWLOCATION , 0 );
318321 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_NOSIGNAL , 0 );
319- ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_TIMEOUT , 10 /* seconds */ );
322+ ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_TIMEOUT , client -> connections > 0 ? 5 : 10 /* seconds */ );
320323 // We know Google supports this, so force it.
321324 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_SSLVERSION , CURL_SSLVERSION_TLSv1_2 );
322325 ASSERT_CURL_EASY_SETOPT (ctx , CURLOPT_ERRORBUFFER , ctx -> curl_errbuf ); // zeroed by calloc
@@ -329,8 +332,13 @@ static void https_fetch_ctx_init(https_client_t *client,
329332 }
330333 CURLMcode multi_code = curl_multi_add_handle (client -> curlm , ctx -> curl );
331334 if (multi_code != CURLM_OK ) {
332- FLOG_REQ ("curl_multi_add_handle error %d: %s" ,
333- multi_code , curl_multi_strerror (multi_code ));
335+ ELOG_REQ ("curl_multi_add_handle error %d: %s" , multi_code , curl_multi_strerror (multi_code ));
336+ if (multi_code == CURLM_ABORTED_BY_CALLBACK ) {
337+ WLOG_REQ ("Resetting HTTPS client to recover from faulty state!" );
338+ https_client_reset (client );
339+ } else {
340+ https_fetch_ctx_cleanup (client , NULL , client -> fetches , -1 ); // dropping current failed request
341+ }
334342 }
335343}
336344
@@ -506,10 +514,10 @@ static void https_fetch_ctx_cleanup(https_client_t *client,
506514 }
507515 int drop_reply = 0 ;
508516 if (curl_result_code < 0 ) {
509- WLOG_REQ ("Request was aborted. " );
517+ WLOG_REQ ("Request was aborted" );
510518 drop_reply = 1 ;
511519 } else if (https_fetch_ctx_process_response (client , ctx , curl_result_code ) != 0 ) {
512- ILOG_REQ ("Response was faulty, skipping DNS reply. " );
520+ ILOG_REQ ("Response was faulty, skipping DNS reply" );
513521 drop_reply = 1 ;
514522 }
515523 if (drop_reply ) {
@@ -545,42 +553,62 @@ static void check_multi_info(https_client_t *c) {
545553 cur = cur -> next ;
546554 }
547555 }
556+ else {
557+ ELOG ("Unhandled curl message: %d" , msg -> msg ); // unlikely
558+ }
548559 }
549560}
550561
551562static void sock_cb (struct ev_loop __attribute__((unused )) * loop ,
552563 struct ev_io * w , int revents ) {
553564 GET_PTR (https_client_t , c , w -> data );
565+ int ignore = 0 ;
554566 CURLMcode code = curl_multi_socket_action (
555567 c -> curlm , w -> fd , (revents & EV_READ ? CURL_CSELECT_IN : 0 ) |
556568 (revents & EV_WRITE ? CURL_CSELECT_OUT : 0 ),
557- & c -> still_running );
558- if (code != CURLM_OK ) {
569+ & ignore );
570+ if (code == CURLM_OK ) {
571+ check_multi_info (c );
572+ }
573+ else {
559574 FLOG ("curl_multi_socket_action error %d: %s" , code , curl_multi_strerror (code ));
575+ if (code == CURLM_ABORTED_BY_CALLBACK ) {
576+ WLOG ("Resetting HTTPS client to recover from faulty state!" );
577+ https_client_reset (c );
578+ }
560579 }
561- check_multi_info (c );
562580}
563581
564582static void timer_cb (struct ev_loop __attribute__((unused )) * loop ,
565583 struct ev_timer * w , int __attribute__((unused )) revents ) {
566584 GET_PTR (https_client_t , c , w -> data );
585+ int ignore = 0 ;
567586 CURLMcode code = curl_multi_socket_action (c -> curlm , CURL_SOCKET_TIMEOUT , 0 ,
568- & c -> still_running );
587+ & ignore );
569588 if (code != CURLM_OK ) {
570- FLOG ("curl_multi_socket_action error %d: %s" , code , curl_multi_strerror (code ));
589+ ELOG ("curl_multi_socket_action error %d: %s" , code , curl_multi_strerror (code ));
571590 }
572591 check_multi_info (c );
573592}
574593
575594static struct ev_io * get_io_event (struct ev_io io_events [], curl_socket_t sock ) {
576- for (int i = 0 ; i < MAX_TOTAL_CONNECTIONS ; i ++ ) {
595+ for (int i = 0 ; i < HTTPS_SOCKET_LIMIT ; i ++ ) {
577596 if (io_events [i ].fd == sock ) {
578597 return & io_events [i ];
579598 }
580599 }
581600 return NULL ;
582601}
583602
603+ static void dump_io_events (struct ev_io io_events []) {
604+ for (int i = 0 ; i < HTTPS_SOCKET_LIMIT ; i ++ ) {
605+ ILOG ("IO event #%d: fd=%d, events=%d/%s%s" ,
606+ i + 1 , io_events [i ].fd , io_events [i ].events ,
607+ (io_events [i ].events & EV_READ ? "R" : "" ),
608+ (io_events [i ].events & EV_WRITE ? "W" : "" ));
609+ }
610+ }
611+
584612static int multi_sock_cb (CURL * curl , curl_socket_t sock , int what ,
585613 void * userp , void __attribute__((unused )) * sockp ) {
586614 GET_PTR (https_client_t , c , userp );
@@ -600,7 +628,10 @@ static int multi_sock_cb(CURL *curl, curl_socket_t sock, int what,
600628 // reserve and start new event on unused slot
601629 io_event_ptr = get_io_event (c -> io_events , 0 );
602630 if (!io_event_ptr ) {
603- FLOG ("curl needed more event, than max connections: %d" , MAX_TOTAL_CONNECTIONS );
631+ ELOG ("curl needed more IO event handler, than the number of maximum sockets: %d" , HTTPS_SOCKET_LIMIT );
632+ dump_io_events (c -> io_events );
633+ logging_flight_recorder_dump ();
634+ return -1 ;
604635 }
605636 DLOG ("Reserved new io event: %p" , io_event_ptr );
606637 // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
@@ -634,14 +665,15 @@ void https_client_init(https_client_t *c, options_t *opt,
634665 "Content-Type: " DOH_CONTENT_TYPE );
635666 c -> fetches = NULL ;
636667 c -> timer .data = c ;
637- for (int i = 0 ; i < MAX_TOTAL_CONNECTIONS ; i ++ ) {
668+ for (int i = 0 ; i < HTTPS_SOCKET_LIMIT ; i ++ ) {
638669 c -> io_events [i ].data = c ;
639670 }
640671 c -> opt = opt ;
641672 c -> stat = stat ;
642673
643674 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_PIPELINING , CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX );
644- ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_MAX_TOTAL_CONNECTIONS , MAX_TOTAL_CONNECTIONS );
675+ ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_MAX_TOTAL_CONNECTIONS , HTTPS_CONNECTION_LIMIT );
676+ ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_MAX_HOST_CONNECTIONS , HTTPS_CONNECTION_LIMIT );
645677 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_SOCKETDATA , c );
646678 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_SOCKETFUNCTION , multi_sock_cb );
647679 ASSERT_CURL_MULTI_SETOPT (c -> curlm , CURLMOPT_TIMERDATA , c );
0 commit comments