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