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) :202 20x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::await_ready() const :214 20x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::await_resume() const :219 20x 100.0% 100.0% boost::corosio::resolver::resolve_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :226 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) :245 13x 100.0% 100.0% boost::corosio::resolver::reverse_resolve_awaitable::await_ready() const :253 13x 100.0% 100.0% boost::corosio::resolver::reverse_resolve_awaitable::await_resume() const :258 13x 100.0% 100.0% boost::corosio::resolver::reverse_resolve_awaitable::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :265 13x 100.0% 82.0% boost::corosio::resolver::resolver<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :296 1x 100.0% 100.0% boost::corosio::resolver::resolver(boost::corosio::resolver&&) :313 1x 100.0% 100.0% boost::corosio::resolver::operator=(boost::corosio::resolver&&) :330 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> >) :365 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) :384 13x 100.0% 100.0% boost::corosio::resolver::resolve(boost::corosio::endpoint const&) :410 5x 100.0% 100.0% boost::corosio::resolver::resolve(boost::corosio::endpoint const&, boost::corosio::reverse_flags) :429 8x 100.0% 100.0% boost::corosio::resolver::get() const :478 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 using exceptions
187 auto results = (co_await r.resolve("www.example.com", "https")).value();
188 @endcode
189 */
190 class BOOST_COROSIO_DECL resolver : public io_object
191 {
192 struct resolve_awaitable
193 {
194 resolver& r_;
195 std::string host_;
196 std::string service_;
197 resolve_flags flags_;
198 std::stop_token token_;
199 mutable std::error_code ec_;
200 mutable resolver_results results_;
201
202 20x resolve_awaitable(
203 resolver& r,
204 std::string_view host,
205 std::string_view service,
206 resolve_flags flags) noexcept
207 20x : r_(r)
208 40x , host_(host)
209 40x , service_(service)
210 20x , flags_(flags)
211 {
212 20x }
213
214 20x bool await_ready() const noexcept
215 {
216 20x return token_.stop_requested();
217 }
218
219 20x capy::io_result<resolver_results> await_resume() const noexcept
220 {
221 20x if (token_.stop_requested())
222 1x return {make_error_code(std::errc::operation_canceled), {}};
223 19x return {ec_, std::move(results_)};
224 }
225
226 20x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
227 -> std::coroutine_handle<>
228 {
229 20x token_ = env->stop_token;
230 60x return r_.get().resolve(
231 20x h, env->executor, host_, service_, flags_, token_, &ec_,
232 40x &results_);
233 }
234 };
235
236 struct reverse_resolve_awaitable
237 {
238 resolver& r_;
239 endpoint ep_;
240 reverse_flags flags_;
241 std::stop_token token_;
242 mutable std::error_code ec_;
243 mutable reverse_resolver_result result_;
244
245 13x reverse_resolve_awaitable(
246 resolver& r, endpoint const& ep, reverse_flags flags) noexcept
247 13x : r_(r)
248 13x , ep_(ep)
249 13x , flags_(flags)
250 {
251 13x }
252
253 13x bool await_ready() const noexcept
254 {
255 13x return token_.stop_requested();
256 }
257
258 13x capy::io_result<reverse_resolver_result> await_resume() const noexcept
259 {
260 13x if (token_.stop_requested())
261 1x return {make_error_code(std::errc::operation_canceled), {}};
262 12x return {ec_, std::move(result_)};
263 }
264
265 13x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
266 -> std::coroutine_handle<>
267 {
268 13x token_ = env->stop_token;
269 26x return r_.get().reverse_resolve(
270 26x h, env->executor, ep_, flags_, token_, &ec_, &result_);
271 }
272 };
273
274 public:
275 /** Destructor.
276
277 Cancels any pending operations.
278 */
279 ~resolver() override;
280
281 /** Construct a resolver from an execution context.
282
283 @param ctx The execution context that will own this resolver.
284 */
285 explicit resolver(capy::execution_context& ctx);
286
287 /** Construct a resolver from an executor.
288
289 The resolver is associated with the executor's context.
290
291 @param ex The executor whose context will own the resolver.
292 */
293 template<class Ex>
294 requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
295 capy::Executor<Ex>
296 1x explicit resolver(Ex const& ex) : resolver(ex.context())
297 {
298 1x }
299
300 /** Move constructor.
301
302 Transfers ownership of the resolver resources. After the move,
303 @p other is in a moved-from state and may only be destroyed or
304 assigned to.
305
306 @param other The resolver to move from.
307
308 @pre No awaitables returned by @p other's `resolve` methods
309 exist.
310 @pre The execution context associated with @p other must
311 outlive this resolver.
312 */
313 1x resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
314
315 /** Move assignment operator.
316
317 Destroys the current implementation and transfers ownership
318 from @p other. After the move, @p other is in a moved-from
319 state and may only be destroyed or assigned to.
320
321 @param other The resolver to move from.
322
323 @pre No awaitables returned by either `*this` or @p other's
324 `resolve` methods exist.
325 @pre The execution context associated with @p other must
326 outlive this resolver.
327
328 @return Reference to this resolver.
329 */
330 2x resolver& operator=(resolver&& other) noexcept
331 {
332 2x if (this != &other)
333 2x h_ = std::move(other.h_);
334 2x return *this;
335 }
336
337 resolver(resolver const&) = delete;
338 resolver& operator=(resolver const&) = delete;
339
340 /** Initiate an asynchronous resolve operation.
341
342 Resolves the host and service names into a list of endpoints.
343
344 This resolver must outlive the returned awaitable.
345
346 @param host A string identifying a location. May be a descriptive
347 name or a numeric address string.
348
349 @param service A string identifying the requested service. This may
350 be a descriptive name or a numeric string corresponding to a
351 port number.
352
353 @return An awaitable that completes with `io_result<resolver_results>`.
354
355 @note `resolver_results` is an alias for `std::vector<resolver_entry>`.
356 Copying it deep-copies every entry (each owns two `std::string`s);
357 move it (`std::move(results)`) or pass iterators when handing it to
358 a by-value sink such as @ref connect.
359
360 @par Example
361 @code
362 auto [ec, results] = co_await r.resolve("www.example.com", "https");
363 @endcode
364 */
365 7x auto resolve(std::string_view host, std::string_view service)
366 {
367 7x return resolve_awaitable(*this, host, service, resolve_flags::none);
368 }
369
370 /** Initiate an asynchronous resolve operation with flags.
371
372 Resolves the host and service names into a list of endpoints.
373
374 This resolver must outlive the returned awaitable.
375
376 @param host A string identifying a location.
377
378 @param service A string identifying the requested service.
379
380 @param flags Flags controlling resolution behavior.
381
382 @return An awaitable that completes with `io_result<resolver_results>`.
383 */
384 13x auto resolve(
385 std::string_view host, std::string_view service, resolve_flags flags)
386 {
387 13x return resolve_awaitable(*this, host, service, flags);
388 }
389
390 /** Initiate an asynchronous reverse resolve operation.
391
392 Resolves an endpoint into a hostname and service name using
393 reverse DNS lookup (PTR record query).
394
395 This resolver must outlive the returned awaitable.
396
397 @param ep The endpoint to resolve.
398
399 @return An awaitable that completes with
400 `io_result<reverse_resolver_result>`.
401
402 @par Example
403 @code
404 endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
405 auto [ec, result] = co_await r.resolve(ep);
406 if (!ec)
407 std::cout << result.host_name() << ":" << result.service_name();
408 @endcode
409 */
410 5x auto resolve(endpoint const& ep)
411 {
412 5x return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
413 }
414
415 /** Initiate an asynchronous reverse resolve operation with flags.
416
417 Resolves an endpoint into a hostname and service name using
418 reverse DNS lookup (PTR record query).
419
420 This resolver must outlive the returned awaitable.
421
422 @param ep The endpoint to resolve.
423
424 @param flags Flags controlling resolution behavior. See reverse_flags.
425
426 @return An awaitable that completes with
427 `io_result<reverse_resolver_result>`.
428 */
429 8x auto resolve(endpoint const& ep, reverse_flags flags)
430 {
431 8x return reverse_resolve_awaitable(*this, ep, flags);
432 }
433
434 /** Cancel any pending asynchronous operations.
435
436 All outstanding operations complete with `errc::operation_canceled`.
437 Check `ec == cond::canceled` for portable comparison.
438 */
439 void cancel();
440
441 public:
442 /** Backend interface for DNS resolution operations.
443
444 Platform backends derive from this to implement forward and
445 reverse DNS resolution via getaddrinfo/getnameinfo.
446 */
447 struct implementation : io_object::implementation
448 {
449 /// Initiate an asynchronous forward DNS resolution.
450 virtual std::coroutine_handle<> resolve(
451 std::coroutine_handle<>,
452 capy::executor_ref,
453 std::string_view host,
454 std::string_view service,
455 resolve_flags flags,
456 std::stop_token,
457 std::error_code*,
458 resolver_results*) = 0;
459
460 /// Initiate an asynchronous reverse DNS resolution.
461 virtual std::coroutine_handle<> reverse_resolve(
462 std::coroutine_handle<>,
463 capy::executor_ref,
464 endpoint const& ep,
465 reverse_flags flags,
466 std::stop_token,
467 std::error_code*,
468 reverse_resolver_result*) = 0;
469
470 /// Cancel pending resolve operations.
471 virtual void cancel() noexcept = 0;
472 };
473
474 protected:
475 explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
476
477 private:
478 37x inline implementation& get() const noexcept
479 {
480 37x return *static_cast<implementation*>(h_.get());
481 }
482 };
483
484 } // namespace boost::corosio
485
486 #endif
487