@@ -50,7 +50,7 @@ struct Zone {
5050};
5151
5252namespace {
53- const constexpr uint64_t MIN_UDP_TIMEOUT_MS = 300 ;
53+ const constexpr uint64_t MIN_QUERY_TIMEOUT_MS = 300 ;
5454const constexpr int MAX_QUERY_DEPTH = 20 ;
5555
5656// https://www.iana.org/domains/root/servers
@@ -78,6 +78,11 @@ const DS ROOT_DS[]
7878 .data = {},
7979 }};
8080
81+ class query_timeout_error : std::runtime_error {
82+ public:
83+ query_timeout_error () : std::runtime_error(" Query timed out" ) {}
84+ };
85+
8186class bad_cookie_error : std::runtime_error {
8287public:
8388 bad_cookie_error () : std::runtime_error(" Bad server cookie" ) {}
@@ -135,8 +140,8 @@ bool address_equals(struct sockaddr_in a, struct sockaddr_in b) {
135140} // namespace
136141
137142Resolver::Resolver (ResolverConfig config)
138- : timeout_duration( config.timeout_ms),
139- udp_timeout_ms(std::max(config.timeout_ms / 5 , MIN_UDP_TIMEOUT_MS) ),
143+ : query_timeout_ms(std::max( config.timeout_ms, MIN_QUERY_TIMEOUT_MS) ),
144+ udp_timeout_ms(query_timeout_ms / 3 ),
140145 port(config.port),
141146 verbose(config.verbose),
142147 enable_rd(config.enable_rd),
@@ -175,7 +180,7 @@ std::optional<std::vector<RR>> Resolver::resolve(const std::string &domain, RRTy
175180 if (rr_type == RRType::RRSIG || rr_type == RRType::OPT) return std::nullopt ;
176181
177182 try {
178- timeout_instant = std::chrono::steady_clock::now () + timeout_duration ;
183+ query_start = std::chrono::steady_clock::now ();
179184 set_socket_timeout (udp_timeout_ms);
180185 return resolve_rec (fully_qualify_domain (domain), rr_type, 0 );
181186 } catch (const std::exception &e) {
@@ -281,10 +286,12 @@ void Resolver::set_socket_timeout(uint64_t timeout_ms) const {
281286}
282287
283288void Resolver::update_timeout () {
284- auto time_left = timeout_instant - std::chrono::steady_clock::now ();
285- auto time_left_ms = std::chrono::duration_cast<std::chrono::duration<uint64_t , std::milli>>(time_left).count ();
286- if (time_left_ms <= 0 ) throw std::runtime_error (" Query timed out" );
287- if (time_left_ms <= udp_timeout_ms) set_socket_timeout (time_left_ms);
289+ auto duration = std::chrono::steady_clock::now () - query_start;
290+ auto duration_ms = std::chrono::duration_cast<std::chrono::duration<uint64_t , std::milli>>(duration).count ();
291+ if (duration_ms >= query_timeout_ms) throw query_timeout_error ();
292+
293+ auto time_left_ms = query_timeout_ms - duration_ms;
294+ if (time_left_ms < udp_timeout_ms) set_socket_timeout (time_left_ms);
288295}
289296
290297void Resolver::udp_send (const std::vector<uint8_t > &buffer, struct sockaddr_in address) {
@@ -643,6 +650,9 @@ std::optional<std::vector<RR>> Resolver::resolve_rec(const std::string &domain,
643650 next_zone = get_next_zone (current_safe_zones);
644651 if (next_zone == nullptr ) return std::nullopt ;
645652 break ;
653+ } catch (const query_timeout_error &) {
654+ if (verbose) std::println (stderr, " Query timed out" );
655+ return std::nullopt ;
646656 } catch (const bad_cookie_error &) {
647657 if (!nameserver->sent_bad_cookie ) {
648658 // Retry the same nameserver with the new server cookie once.
0 commit comments