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