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