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