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