include/boost/corosio/resolver.hpp

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