include/boost/corosio/resolver.hpp

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