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