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