include/boost/corosio/resolver.hpp

100.0% Lines (69/69) 100.0% List of functions (24/24)
resolver.hpp
f(x) Functions (24)
Function Calls Lines Blocks
boost::corosio::operator|(boost::corosio::resolve_flags, boost::corosio::resolve_flags) :73 14x 100.0% 100.0% boost::corosio::operator|=(boost::corosio::resolve_flags&, boost::corosio::resolve_flags) :81 1x 100.0% 100.0% boost::corosio::operator&(boost::corosio::resolve_flags, boost::corosio::resolve_flags) :89 133x 100.0% 100.0% boost::corosio::operator&=(boost::corosio::resolve_flags&, boost::corosio::resolve_flags) :97 1x 100.0% 100.0% boost::corosio::operator|(boost::corosio::reverse_flags, boost::corosio::reverse_flags) :127 8x 100.0% 100.0% boost::corosio::operator|=(boost::corosio::reverse_flags&, boost::corosio::reverse_flags) :135 1x 100.0% 100.0% boost::corosio::operator&(boost::corosio::reverse_flags, boost::corosio::reverse_flags) :143 55x 100.0% 100.0% boost::corosio::operator&=(boost::corosio::reverse_flags&, boost::corosio::reverse_flags) :151 1x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::resolve_awaitable(boost::corosio::resolver&, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, boost::corosio::resolve_flags) :204 20x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::await_ready() const :216 20x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::await_resume() const :221 20x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :228 20x 100.0% 85.0% boost::corosio::resolver::reverse_resolve_awaitable::reverse_resolve_awaitable(boost::corosio::resolver&, boost::corosio::endpoint const&, boost::corosio::reverse_flags) :247 13x 100.0% 100.0% boost::corosio::resolver::reverse_resolve_awaitable::await_ready() const :255 13x 100.0% 100.0% boost::corosio::resolver::reverse_resolve_awaitable::await_resume() const :260 13x 100.0% 100.0% boost::corosio::resolver::reverse_resolve_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :267 13x 100.0% 82.0% boost::corosio::resolver::resolver<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :298 1x 100.0% 100.0% boost::corosio::resolver::resolver(boost::corosio::resolver&&) :315 1x 100.0% 100.0% boost::corosio::resolver::operator=(boost::corosio::resolver&&) :332 2x 100.0% 100.0% boost::corosio::resolver::resolve(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >) :367 7x 100.0% 100.0% boost::corosio::resolver::resolve(std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, boost::corosio::resolve_flags) :386 13x 100.0% 100.0% boost::corosio::resolver::resolve(boost::corosio::endpoint const&) :412 5x 100.0% 100.0% boost::corosio::resolver::resolve(boost::corosio::endpoint const&, boost::corosio::reverse_flags) :431 8x 100.0% 100.0% boost::corosio::resolver::get() const :480 37x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #ifndef BOOST_COROSIO_RESOLVER_HPP
13 #define BOOST_COROSIO_RESOLVER_HPP
14
15 #include <boost/corosio/detail/config.hpp>
16 #include <boost/corosio/endpoint.hpp>
17 #include <boost/corosio/io/io_object.hpp>
18 #include <boost/capy/io_result.hpp>
19 #include <boost/corosio/resolver_results.hpp>
20 #include <boost/capy/ex/executor_ref.hpp>
21 #include <boost/capy/ex/execution_context.hpp>
22 #include <boost/capy/ex/io_env.hpp>
23 #include <boost/capy/concept/executor.hpp>
24
25 #include <system_error>
26
27 #include <cassert>
28 #include <concepts>
29 #include <coroutine>
30 #include <stop_token>
31 #include <string>
32 #include <string_view>
33 #include <type_traits>
34
35 namespace boost::corosio {
36
37 /** Bitmask flags for resolver queries.
38
39 These flags correspond to the hints parameter of getaddrinfo.
40 */
41 enum class resolve_flags : unsigned int
42 {
43 /// No flags.
44 none = 0,
45
46 /// Indicate that returned endpoint is intended for use as a locally
47 /// bound socket endpoint.
48 passive = 0x01,
49
50 /// Host name should be treated as a numeric string defining an IPv4
51 /// or IPv6 address and no name resolution should be attempted.
52 numeric_host = 0x04,
53
54 /// Service name should be treated as a numeric string defining a port
55 /// number and no name resolution should be attempted.
56 numeric_service = 0x08,
57
58 /// Only return IPv4 addresses if a non-loopback IPv4 address is
59 /// configured for the system. Only return IPv6 addresses if a
60 /// non-loopback IPv6 address is configured for the system.
61 address_configured = 0x20,
62
63 /// If the query protocol family is specified as IPv6, return
64 /// IPv4-mapped IPv6 addresses on finding no IPv6 addresses.
65 v4_mapped = 0x800,
66
67 /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses.
68 all_matching = 0x100
69 };
70
71 /** Combine two resolve_flags. */
72 inline resolve_flags
73 14x operator|(resolve_flags a, resolve_flags b) noexcept
74 {
75 return static_cast<resolve_flags>(
76 14x static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
77 }
78
79 /** Combine two resolve_flags. */
80 inline resolve_flags&
81 1x operator|=(resolve_flags& a, resolve_flags b) noexcept
82 {
83 1x a = a | b;
84 1x return a;
85 }
86
87 /** Intersect two resolve_flags. */
88 inline resolve_flags
89 133x operator&(resolve_flags a, resolve_flags b) noexcept
90 {
91 return static_cast<resolve_flags>(
92 133x static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
93 }
94
95 /** Intersect two resolve_flags. */
96 inline resolve_flags&
97 1x operator&=(resolve_flags& a, resolve_flags b) noexcept
98 {
99 1x a = a & b;
100 1x return a;
101 }
102
103 /** Bitmask flags for reverse resolver queries.
104
105 These flags correspond to the flags parameter of getnameinfo.
106 */
107 enum class reverse_flags : unsigned int
108 {
109 /// No flags.
110 none = 0,
111
112 /// Return the numeric form of the hostname instead of its name.
113 numeric_host = 0x01,
114
115 /// Return the numeric form of the service name instead of its name.
116 numeric_service = 0x02,
117
118 /// Return an error if the hostname cannot be resolved.
119 name_required = 0x04,
120
121 /// Lookup for datagram (UDP) service instead of stream (TCP).
122 datagram_service = 0x08
123 };
124
125 /** Combine two reverse_flags. */
126 inline reverse_flags
127 8x operator|(reverse_flags a, reverse_flags b) noexcept
128 {
129 return static_cast<reverse_flags>(
130 8x static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
131 }
132
133 /** Combine two reverse_flags. */
134 inline reverse_flags&
135 1x operator|=(reverse_flags& a, reverse_flags b) noexcept
136 {
137 1x a = a | b;
138 1x return a;
139 }
140
141 /** Intersect two reverse_flags. */
142 inline reverse_flags
143 55x operator&(reverse_flags a, reverse_flags b) noexcept
144 {
145 return static_cast<reverse_flags>(
146 55x static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
147 }
148
149 /** Intersect two reverse_flags. */
150 inline reverse_flags&
151 1x operator&=(reverse_flags& a, reverse_flags b) noexcept
152 {
153 1x a = a & b;
154 1x return a;
155 }
156
157 /** An asynchronous DNS resolver for coroutine I/O.
158
159 This class provides asynchronous DNS resolution operations that return
160 awaitable types. Each operation participates in the affine awaitable
161 protocol, ensuring coroutines resume on the correct executor.
162
163 @par Thread Safety
164 Distinct objects: Safe.@n
165 Shared objects: Unsafe. A resolver must not have concurrent resolve
166 operations.
167
168 @par Semantics
169 Wraps platform DNS resolution (getaddrinfo/getnameinfo).
170 Operations dispatch to OS resolver APIs via the io_context
171 thread pool.
172
173 @par Example
174 @code
175 io_context ioc;
176 resolver r(ioc);
177
178 // Using structured bindings
179 auto [ec, results] = co_await r.resolve("www.example.com", "https");
180 if (ec)
181 co_return;
182
183 for (auto const& entry : results)
184 std::cout << entry.get_endpoint().port() << std::endl;
185
186 // Or, to convert errors into exceptions:
187 auto [ec2, results2] = co_await r.resolve("www.example.com", "https");
188 if (ec2)
189 throw std::system_error(ec2);
190 @endcode
191 */
192 class BOOST_COROSIO_DECL resolver : public io_object
193 {
194 struct resolve_awaitable
195 {
196 resolver& r_;
197 std::string host_;
198 std::string service_;
199 resolve_flags flags_;
200 std::stop_token token_;
201 mutable std::error_code ec_;
202 mutable resolver_results results_;
203
204 20x resolve_awaitable(
205 resolver& r,
206 std::string_view host,
207 std::string_view service,
208 resolve_flags flags) noexcept
209 20x : r_(r)
210 40x , host_(host)
211 40x , service_(service)
212 20x , flags_(flags)
213 {
214 20x }
215
216 20x bool await_ready() const noexcept
217 {
218 20x return token_.stop_requested();
219 }
220
221 20x capy::io_result<resolver_results> await_resume() const noexcept
222 {
223 20x if (token_.stop_requested())
224 1x return {make_error_code(std::errc::operation_canceled), {}};
225 19x return {ec_, std::move(results_)};
226 }
227
228 20x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
229 -> std::coroutine_handle<>
230 {
231 20x token_ = env->stop_token;
232 60x return r_.get().resolve(
233 20x h, env->executor, host_, service_, flags_, token_, &ec_,
234 40x &results_);
235 }
236 };
237
238 struct reverse_resolve_awaitable
239 {
240 resolver& r_;
241 endpoint ep_;
242 reverse_flags flags_;
243 std::stop_token token_;
244 mutable std::error_code ec_;
245 mutable reverse_resolver_result result_;
246
247 13x reverse_resolve_awaitable(
248 resolver& r, endpoint const& ep, reverse_flags flags) noexcept
249 13x : r_(r)
250 13x , ep_(ep)
251 13x , flags_(flags)
252 {
253 13x }
254
255 13x bool await_ready() const noexcept
256 {
257 13x return token_.stop_requested();
258 }
259
260 13x capy::io_result<reverse_resolver_result> await_resume() const noexcept
261 {
262 13x if (token_.stop_requested())
263 1x return {make_error_code(std::errc::operation_canceled), {}};
264 12x return {ec_, std::move(result_)};
265 }
266
267 13x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
268 -> std::coroutine_handle<>
269 {
270 13x token_ = env->stop_token;
271 26x return r_.get().reverse_resolve(
272 26x h, env->executor, ep_, flags_, token_, &ec_, &result_);
273 }
274 };
275
276 public:
277 /** Destructor.
278
279 Cancels any pending operations.
280 */
281 ~resolver() override;
282
283 /** Construct a resolver from an execution context.
284
285 @param ctx The execution context that will own this resolver.
286 */
287 explicit resolver(capy::execution_context& ctx);
288
289 /** Construct a resolver from an executor.
290
291 The resolver is associated with the executor's context.
292
293 @param ex The executor whose context will own the resolver.
294 */
295 template<class Ex>
296 requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
297 capy::Executor<Ex>
298 1x explicit resolver(Ex const& ex) : resolver(ex.context())
299 {
300 1x }
301
302 /** Move constructor.
303
304 Transfers ownership of the resolver resources. After the move,
305 @p other is in a moved-from state and may only be destroyed or
306 assigned to.
307
308 @param other The resolver to move from.
309
310 @pre No awaitables returned by @p other's `resolve` methods
311 exist.
312 @pre The execution context associated with @p other must
313 outlive this resolver.
314 */
315 1x resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
316
317 /** Move assignment operator.
318
319 Destroys the current implementation and transfers ownership
320 from @p other. After the move, @p other is in a moved-from
321 state and may only be destroyed or assigned to.
322
323 @param other The resolver to move from.
324
325 @pre No awaitables returned by either `*this` or @p other's
326 `resolve` methods exist.
327 @pre The execution context associated with @p other must
328 outlive this resolver.
329
330 @return Reference to this resolver.
331 */
332 2x resolver& operator=(resolver&& other) noexcept
333 {
334 2x if (this != &other)
335 2x h_ = std::move(other.h_);
336 2x return *this;
337 }
338
339 resolver(resolver const&) = delete;
340 resolver& operator=(resolver const&) = delete;
341
342 /** Initiate an asynchronous resolve operation.
343
344 Resolves the host and service names into a list of endpoints.
345
346 This resolver must outlive the returned awaitable.
347
348 @param host A string identifying a location. May be a descriptive
349 name or a numeric address string.
350
351 @param service A string identifying the requested service. This may
352 be a descriptive name or a numeric string corresponding to a
353 port number.
354
355 @return An awaitable that completes with `io_result<resolver_results>`.
356
357 @note `resolver_results` is an alias for `std::vector<resolver_entry>`.
358 Copying it deep-copies every entry (each owns two `std::string`s);
359 move it (`std::move(results)`) or pass iterators when handing it to
360 a by-value sink such as @ref connect.
361
362 @par Example
363 @code
364 auto [ec, results] = co_await r.resolve("www.example.com", "https");
365 @endcode
366 */
367 7x auto resolve(std::string_view host, std::string_view service)
368 {
369 7x return resolve_awaitable(*this, host, service, resolve_flags::none);
370 }
371
372 /** Initiate an asynchronous resolve operation with flags.
373
374 Resolves the host and service names into a list of endpoints.
375
376 This resolver must outlive the returned awaitable.
377
378 @param host A string identifying a location.
379
380 @param service A string identifying the requested service.
381
382 @param flags Flags controlling resolution behavior.
383
384 @return An awaitable that completes with `io_result<resolver_results>`.
385 */
386 13x auto resolve(
387 std::string_view host, std::string_view service, resolve_flags flags)
388 {
389 13x return resolve_awaitable(*this, host, service, flags);
390 }
391
392 /** Initiate an asynchronous reverse resolve operation.
393
394 Resolves an endpoint into a hostname and service name using
395 reverse DNS lookup (PTR record query).
396
397 This resolver must outlive the returned awaitable.
398
399 @param ep The endpoint to resolve.
400
401 @return An awaitable that completes with
402 `io_result<reverse_resolver_result>`.
403
404 @par Example
405 @code
406 endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
407 auto [ec, result] = co_await r.resolve(ep);
408 if (!ec)
409 std::cout << result.host_name() << ":" << result.service_name();
410 @endcode
411 */
412 5x auto resolve(endpoint const& ep)
413 {
414 5x return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
415 }
416
417 /** Initiate an asynchronous reverse resolve operation with flags.
418
419 Resolves an endpoint into a hostname and service name using
420 reverse DNS lookup (PTR record query).
421
422 This resolver must outlive the returned awaitable.
423
424 @param ep The endpoint to resolve.
425
426 @param flags Flags controlling resolution behavior. See reverse_flags.
427
428 @return An awaitable that completes with
429 `io_result<reverse_resolver_result>`.
430 */
431 8x auto resolve(endpoint const& ep, reverse_flags flags)
432 {
433 8x return reverse_resolve_awaitable(*this, ep, flags);
434 }
435
436 /** Cancel any pending asynchronous operations.
437
438 All outstanding operations complete with `errc::operation_canceled`.
439 Check `ec == cond::canceled` for portable comparison.
440 */
441 void cancel();
442
443 public:
444 /** Backend interface for DNS resolution operations.
445
446 Platform backends derive from this to implement forward and
447 reverse DNS resolution via getaddrinfo/getnameinfo.
448 */
449 struct implementation : io_object::implementation
450 {
451 /// Initiate an asynchronous forward DNS resolution.
452 virtual std::coroutine_handle<> resolve(
453 std::coroutine_handle<>,
454 capy::executor_ref,
455 std::string_view host,
456 std::string_view service,
457 resolve_flags flags,
458 std::stop_token,
459 std::error_code*,
460 resolver_results*) = 0;
461
462 /// Initiate an asynchronous reverse DNS resolution.
463 virtual std::coroutine_handle<> reverse_resolve(
464 std::coroutine_handle<>,
465 capy::executor_ref,
466 endpoint const& ep,
467 reverse_flags flags,
468 std::stop_token,
469 std::error_code*,
470 reverse_resolver_result*) = 0;
471
472 /// Cancel pending resolve operations.
473 virtual void cancel() noexcept = 0;
474 };
475
476 protected:
477 explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
478
479 private:
480 37x inline implementation& get() const noexcept
481 {
482 37x return *static_cast<implementation*>(h_.get());
483 }
484 };
485
486 } // namespace boost::corosio
487
488 #endif
489