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