96.25% Lines (77/80) 100.00% Functions (25/25)
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/detail/op_base.hpp>
17   #include <boost/corosio/endpoint.hpp> 17   #include <boost/corosio/endpoint.hpp>
18   #include <boost/corosio/io/io_object.hpp> 18   #include <boost/corosio/io/io_object.hpp>
19   #include <boost/capy/results.hpp> 19   #include <boost/capy/results.hpp>
20   #include <boost/capy/ex/executor_ref.hpp> 20   #include <boost/capy/ex/executor_ref.hpp>
21   #include <boost/capy/ex/execution_context.hpp> 21   #include <boost/capy/ex/execution_context.hpp>
22   #include <boost/capy/ex/await_context.hpp> 22   #include <boost/capy/ex/await_context.hpp>
23   #include <boost/capy/concept/executor.hpp> 23   #include <boost/capy/concept/executor.hpp>
24   24  
25   #include <system_error> 25   #include <system_error>
26   26  
27   #include <cassert> 27   #include <cassert>
28   #include <concepts> 28   #include <concepts>
29   #include <boost/capy/continuation.hpp> 29   #include <boost/capy/continuation.hpp>
30   30  
31   #include <coroutine> 31   #include <coroutine>
32   #include <stop_token> 32   #include <stop_token>
33   #include <string> 33   #include <string>
34   #include <string_view> 34   #include <string_view>
35   #include <vector> 35   #include <vector>
36   #include <type_traits> 36   #include <type_traits>
37   37  
38   namespace boost::corosio { 38   namespace boost::corosio {
39   39  
40   /** Bitmask flags for resolver queries. 40   /** Bitmask flags for resolver queries.
41   41  
42   These flags correspond to the hints parameter of `getaddrinfo`. 42   These flags correspond to the hints parameter of `getaddrinfo`.
43   */ 43   */
44   enum class resolve_flags : unsigned int 44   enum class resolve_flags : unsigned int
45   { 45   {
46   /// No flags. 46   /// No flags.
47   none = 0, 47   none = 0,
48   48  
49   /// Indicate that returned endpoint is intended for use as a locally 49   /// Indicate that returned endpoint is intended for use as a locally
50   /// bound socket endpoint. 50   /// bound socket endpoint.
51   passive = 0x01, 51   passive = 0x01,
52   52  
53   /// Host name should be treated as a numeric string defining an IPv4 53   /// Host name should be treated as a numeric string defining an IPv4
54   /// or IPv6 address and no name resolution should be attempted. 54   /// or IPv6 address and no name resolution should be attempted.
55   numeric_host = 0x04, 55   numeric_host = 0x04,
56   56  
57   /// Service name should be treated as a numeric string defining a port 57   /// Service name should be treated as a numeric string defining a port
58   /// number and no name resolution should be attempted. 58   /// number and no name resolution should be attempted.
59   numeric_service = 0x08, 59   numeric_service = 0x08,
60   60  
61   /// Only return IPv4 addresses if a non-loopback IPv4 address is 61   /// Only return IPv4 addresses if a non-loopback IPv4 address is
62   /// configured for the system. Only return IPv6 addresses if a 62   /// configured for the system. Only return IPv6 addresses if a
63   /// non-loopback IPv6 address is configured for the system. 63   /// non-loopback IPv6 address is configured for the system.
64   address_configured = 0x20, 64   address_configured = 0x20,
65   65  
66   /// If the query protocol family is specified as IPv6, return 66   /// If the query protocol family is specified as IPv6, return
67   /// IPv4-mapped IPv6 addresses on finding no IPv6 addresses. 67   /// IPv4-mapped IPv6 addresses on finding no IPv6 addresses.
68   v4_mapped = 0x800, 68   v4_mapped = 0x800,
69   69  
70   /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses. 70   /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses.
71   all_matching = 0x100 71   all_matching = 0x100
72   }; 72   };
73   73  
74   /** Combine two `resolve_flags`. */ 74   /** Combine two `resolve_flags`. */
75   inline resolve_flags 75   inline resolve_flags
HITCBC 76   23 operator|(resolve_flags a, resolve_flags b) noexcept 76   23 operator|(resolve_flags a, resolve_flags b) noexcept
77   { 77   {
78   return static_cast<resolve_flags>( 78   return static_cast<resolve_flags>(
HITCBC 79   23 static_cast<unsigned int>(a) | static_cast<unsigned int>(b)); 79   23 static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
80   } 80   }
81   81  
82   /** Combine two `resolve_flags`. */ 82   /** Combine two `resolve_flags`. */
83   inline resolve_flags& 83   inline resolve_flags&
HITCBC 84   1 operator|=(resolve_flags& a, resolve_flags b) noexcept 84   1 operator|=(resolve_flags& a, resolve_flags b) noexcept
85   { 85   {
HITCBC 86   1 a = a | b; 86   1 a = a | b;
HITCBC 87   1 return a; 87   1 return a;
88   } 88   }
89   89  
90   /** Intersect two `resolve_flags`. */ 90   /** Intersect two `resolve_flags`. */
91   inline resolve_flags 91   inline resolve_flags
HITCBC 92   235 operator&(resolve_flags a, resolve_flags b) noexcept 92   235 operator&(resolve_flags a, resolve_flags b) noexcept
93   { 93   {
94   return static_cast<resolve_flags>( 94   return static_cast<resolve_flags>(
HITCBC 95   235 static_cast<unsigned int>(a) & static_cast<unsigned int>(b)); 95   235 static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
96   } 96   }
97   97  
98   /** Intersect two `resolve_flags`. */ 98   /** Intersect two `resolve_flags`. */
99   inline resolve_flags& 99   inline resolve_flags&
HITCBC 100   1 operator&=(resolve_flags& a, resolve_flags b) noexcept 100   1 operator&=(resolve_flags& a, resolve_flags b) noexcept
101   { 101   {
HITCBC 102   1 a = a & b; 102   1 a = a & b;
HITCBC 103   1 return a; 103   1 return a;
104   } 104   }
105   105  
106   /** Bitmask flags for reverse resolver queries. 106   /** Bitmask flags for reverse resolver queries.
107   107  
108   These flags correspond to the flags parameter of `getnameinfo`. 108   These flags correspond to the flags parameter of `getnameinfo`.
109   */ 109   */
110   enum class reverse_flags : unsigned int 110   enum class reverse_flags : unsigned int
111   { 111   {
112   /// No flags. 112   /// No flags.
113   none = 0, 113   none = 0,
114   114  
115   /// Return the numeric form of the hostname instead of its name. 115   /// Return the numeric form of the hostname instead of its name.
116   numeric_host = 0x01, 116   numeric_host = 0x01,
117   117  
118   /// Return the numeric form of the service name instead of its name. 118   /// Return the numeric form of the service name instead of its name.
119   numeric_service = 0x02, 119   numeric_service = 0x02,
120   120  
121   /// Return an error if the hostname cannot be resolved. 121   /// Return an error if the hostname cannot be resolved.
122   name_required = 0x04, 122   name_required = 0x04,
123   123  
124   /// Lookup for datagram (UDP) service instead of stream (TCP). 124   /// Lookup for datagram (UDP) service instead of stream (TCP).
125   datagram_service = 0x08 125   datagram_service = 0x08
126   }; 126   };
127   127  
128   /** Combine two `reverse_flags`. */ 128   /** Combine two `reverse_flags`. */
129   inline reverse_flags 129   inline reverse_flags
HITCBC 130   11 operator|(reverse_flags a, reverse_flags b) noexcept 130   11 operator|(reverse_flags a, reverse_flags b) noexcept
131   { 131   {
132   return static_cast<reverse_flags>( 132   return static_cast<reverse_flags>(
HITCBC 133   11 static_cast<unsigned int>(a) | static_cast<unsigned int>(b)); 133   11 static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
134   } 134   }
135   135  
136   /** Combine two `reverse_flags`. */ 136   /** Combine two `reverse_flags`. */
137   inline reverse_flags& 137   inline reverse_flags&
HITCBC 138   1 operator|=(reverse_flags& a, reverse_flags b) noexcept 138   1 operator|=(reverse_flags& a, reverse_flags b) noexcept
139   { 139   {
HITCBC 140   1 a = a | b; 140   1 a = a | b;
HITCBC 141   1 return a; 141   1 return a;
142   } 142   }
143   143  
144   /** Intersect two `reverse_flags`. */ 144   /** Intersect two `reverse_flags`. */
145   inline reverse_flags 145   inline reverse_flags
HITCBC 146   71 operator&(reverse_flags a, reverse_flags b) noexcept 146   71 operator&(reverse_flags a, reverse_flags b) noexcept
147   { 147   {
148   return static_cast<reverse_flags>( 148   return static_cast<reverse_flags>(
HITCBC 149   71 static_cast<unsigned int>(a) & static_cast<unsigned int>(b)); 149   71 static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
150   } 150   }
151   151  
152   /** Intersect two `reverse_flags`. */ 152   /** Intersect two `reverse_flags`. */
153   inline reverse_flags& 153   inline reverse_flags&
HITCBC 154   1 operator&=(reverse_flags& a, reverse_flags b) noexcept 154   1 operator&=(reverse_flags& a, reverse_flags b) noexcept
155   { 155   {
HITCBC 156   1 a = a & b; 156   1 a = a & b;
HITCBC 157   1 return a; 157   1 return a;
158   } 158   }
159   159  
160   /** The name of an endpoint. 160   /** The name of an endpoint.
161   161  
162   Reverse resolution translates an endpoint into its symbolic 162   Reverse resolution translates an endpoint into its symbolic
163   spelling: the host name and the service name. Both fields carry 163   spelling: the host name and the service name. Both fields carry
164   resolved data; the endpoint they name is the one the caller 164   resolved data; the endpoint they name is the one the caller
165   passed to `resolve`. 165   passed to `resolve`.
166   */ 166   */
167   struct endpoint_name 167   struct endpoint_name
168   { 168   {
169   /// The resolved host name. 169   /// The resolved host name.
170   std::string host_name; 170   std::string host_name;
171   171  
172   /// The resolved service name. 172   /// The resolved service name.
173   std::string service_name; 173   std::string service_name;
174   }; 174   };
175   175  
176   /** Resolves host names and services to endpoints, from a coroutine. 176   /** Resolves host names and services to endpoints, from a coroutine.
177   177  
178   This class provides asynchronous DNS resolution operations that return 178   This class provides asynchronous DNS resolution operations that return
179   awaitable types. Each operation participates in the affine awaitable 179   awaitable types. Each operation participates in the affine awaitable
180   protocol, ensuring coroutines resume on the correct executor. 180   protocol, ensuring coroutines resume on the correct executor.
181   181  
182   @par Thread Safety 182   @par Thread Safety
183   Distinct objects: Safe.@n 183   Distinct objects: Safe.@n
184   Shared objects: Unsafe. A resolver must not have concurrent resolve 184   Shared objects: Unsafe. A resolver must not have concurrent resolve
185   operations. 185   operations.
186   186  
187   @par Semantics 187   @par Semantics
188   Wraps platform DNS resolution (`getaddrinfo`/`getnameinfo`). 188   Wraps platform DNS resolution (`getaddrinfo`/`getnameinfo`).
189   Operations dispatch to OS resolver APIs via the `io_context` 189   Operations dispatch to OS resolver APIs via the `io_context`
190   thread pool. 190   thread pool.
191   191  
192   @par Example 192   @par Example
193   @par !example resolver 193   @par !example resolver
194   */ 194   */
195   class BOOST_COROSIO_DECL resolver : public io_object 195   class BOOST_COROSIO_DECL resolver : public io_object
196   { 196   {
197   struct resolve_awaitable 197   struct resolve_awaitable
198   : detail::value_op_base<resolve_awaitable, std::vector<endpoint>> 198   : detail::value_op_base<resolve_awaitable, std::vector<endpoint>>
199   { 199   {
200   private: 200   private:
201   friend resolver; 201   friend resolver;
202   202  
HITCBC 203   37 resolve_awaitable( 203   37 resolve_awaitable(
204   resolver& r, 204   resolver& r,
205   std::string_view host, 205   std::string_view host,
206   std::string_view service, 206   std::string_view service,
207   resolve_flags flags) noexcept 207   resolve_flags flags) noexcept
HITCBC 208   74 : r_(r) 208   74 : r_(r)
HITCBC 209   74 , host_(host) 209   74 , host_(host)
HITCBC 210   74 , service_(service) 210   74 , service_(service)
HITCBC 211   37 , flags_(flags) 211   37 , flags_(flags)
212   { 212   {
HITCBC 213   37 } 213   37 }
214   214  
215   friend detail::value_op_base<resolve_awaitable, std::vector<endpoint>>; 215   friend detail::value_op_base<resolve_awaitable, std::vector<endpoint>>;
216   resolver& r_; 216   resolver& r_;
217   std::string host_; 217   std::string host_;
218   std::string service_; 218   std::string service_;
219   resolve_flags flags_; 219   resolve_flags flags_;
220   220  
221   std::coroutine_handle<> 221   std::coroutine_handle<>
HITCBC 222   36 dispatch(capy::continuation& cont, capy::executor_ref ex) const 222   36 dispatch(capy::continuation& cont, capy::executor_ref ex) const
223   { 223   {
HITCBC 224   108 return r_.get().resolve( 224   108 return r_.get().resolve(
HITCBC 225   108 cont, ex, host_, service_, flags_, token_, &ec_, &value_); 225   108 cont, ex, host_, service_, flags_, token_, &ec_, &value_);
226   } 226   }
227   }; 227   };
228   228  
229   struct resolve_host_awaitable 229   struct resolve_host_awaitable
230   : detail::value_op_base<resolve_host_awaitable, std::vector<endpoint>> 230   : detail::value_op_base<resolve_host_awaitable, std::vector<endpoint>>
231   { 231   {
232   private: 232   private:
233   friend resolver; 233   friend resolver;
234   234  
HITCBC 235   6 resolve_host_awaitable( 235   6 resolve_host_awaitable(
236   resolver& r, std::string_view host, resolve_flags flags) noexcept 236   resolver& r, std::string_view host, resolve_flags flags) noexcept
HITCBC 237   12 : r_(r) 237   12 : r_(r)
HITCBC 238   12 , host_(host) 238   12 , host_(host)
HITCBC 239   6 , flags_(flags) 239   6 , flags_(flags)
240   { 240   {
HITCBC 241   6 } 241   6 }
242   242  
243   friend detail:: 243   friend detail::
244   value_op_base<resolve_host_awaitable, std::vector<endpoint>>; 244   value_op_base<resolve_host_awaitable, std::vector<endpoint>>;
245   resolver& r_; 245   resolver& r_;
246   std::string host_; 246   std::string host_;
247   resolve_flags flags_; 247   resolve_flags flags_;
248   248  
249   std::coroutine_handle<> 249   std::coroutine_handle<>
HITCBC 250   5 dispatch(capy::continuation& cont, capy::executor_ref ex) const 250   5 dispatch(capy::continuation& cont, capy::executor_ref ex) const
251   { 251   {
252   // An empty service reaches the system resolver as null, 252   // An empty service reaches the system resolver as null,
253   // which is the host-only query 253   // which is the host-only query
HITCBC 254   15 return r_.get().resolve( 254   15 return r_.get().resolve(
HITCBC 255   15 cont, ex, host_, {}, flags_, token_, &ec_, &value_); 255   15 cont, ex, host_, {}, flags_, token_, &ec_, &value_);
256   } 256   }
257   257  
258   public: 258   public:
259   // Shadows the base: the endpoint result is reshaped into 259   // Shadows the base: the endpoint result is reshaped into
260   // the honest address list 260   // the honest address list
261   [[nodiscard]] capy::results<std::vector<ip_address>> 261   [[nodiscard]] capy::results<std::vector<ip_address>>
HITCBC 262   6 await_resume() const 262   6 await_resume() const
263   { 263   {
HITCBC 264   6 std::vector<ip_address> addrs; 264   6 std::vector<ip_address> addrs;
HITCBC 265   6 addrs.reserve(value_.size()); 265   6 addrs.reserve(value_.size());
HITCBC 266   9 for (auto const& entry : value_) 266   9 for (auto const& entry : value_)
267   { 267   {
HITCBC 268   3 auto a = entry.address(); 268   3 auto a = entry.address();
HITCBC 269   3 bool duplicate = false; 269   3 bool duplicate = false;
HITCBC 270   3 for (auto const& seen : addrs) 270   3 for (auto const& seen : addrs)
271   { 271   {
MISUBC 272   ✗ if (seen == a) 272   ✗ if (seen == a)
273   { 273   {
MISUBC 274   ✗ duplicate = true; 274   ✗ duplicate = true;
MISUBC 275   ✗ break; 275   ✗ break;
276   } 276   }
277   } 277   }
278   // The same address can come back more than once 278   // The same address can come back more than once
279   // (mixed name sources, repeated records); each 279   // (mixed name sources, repeated records); each
280   // address is reported once 280   // address is reported once
HITCBC 281   3 if (!duplicate) 281   3 if (!duplicate)
HITCBC 282   3 addrs.push_back(a); 282   3 addrs.push_back(a);
283   } 283   }
HITCBC 284   12 return {ec_, std::move(addrs)}; 284   12 return {ec_, std::move(addrs)};
HITCBC 285   6 } 285   6 }
286   }; 286   };
287   287  
288   struct reverse_resolve_awaitable 288   struct reverse_resolve_awaitable
289   : detail::value_op_base<reverse_resolve_awaitable, endpoint_name> 289   : detail::value_op_base<reverse_resolve_awaitable, endpoint_name>
290   { 290   {
291   private: 291   private:
292   friend resolver; 292   friend resolver;
293   293  
HITCBC 294   22 reverse_resolve_awaitable( 294   22 reverse_resolve_awaitable(
295   resolver& r, endpoint const& ep, reverse_flags flags) noexcept 295   resolver& r, endpoint const& ep, reverse_flags flags) noexcept
HITCBC 296   44 : r_(r) 296   44 : r_(r)
HITCBC 297   22 , ep_(ep) 297   22 , ep_(ep)
HITCBC 298   22 , flags_(flags) 298   22 , flags_(flags)
299   { 299   {
HITCBC 300   22 } 300   22 }
301   301  
302   friend detail::value_op_base<reverse_resolve_awaitable, endpoint_name>; 302   friend detail::value_op_base<reverse_resolve_awaitable, endpoint_name>;
303   303  
304   resolver& r_; 304   resolver& r_;
305   endpoint ep_; 305   endpoint ep_;
306   reverse_flags flags_; 306   reverse_flags flags_;
307   307  
308   std::coroutine_handle<> 308   std::coroutine_handle<>
HITCBC 309   21 dispatch(capy::continuation& cont, capy::executor_ref ex) const 309   21 dispatch(capy::continuation& cont, capy::executor_ref ex) const
310   { 310   {
HITCBC 311   42 return r_.get().reverse_resolve( 311   42 return r_.get().reverse_resolve(
HITCBC 312   42 cont, ex, ep_, flags_, token_, &ec_, &value_); 312   42 cont, ex, ep_, flags_, token_, &ec_, &value_);
313   } 313   }
314   }; 314   };
315   315  
316   public: 316   public:
317   /** Destructor. 317   /** Destructor.
318   318  
319   Cancels any pending operations. 319   Cancels any pending operations.
320   */ 320   */
321   ~resolver() override; 321   ~resolver() override;
322   322  
323   /** Construct a resolver from an execution context. 323   /** Construct a resolver from an execution context.
324   324  
325   @param ctx The execution context that owns this resolver. 325   @param ctx The execution context that owns this resolver.
326   */ 326   */
327   explicit resolver(capy::execution_context& ctx); 327   explicit resolver(capy::execution_context& ctx);
328   328  
329   /** Construct a resolver from an executor. 329   /** Construct a resolver from an executor.
330   330  
331   The resolver is associated with the executor's context. 331   The resolver is associated with the executor's context.
332   332  
333   @param ex The executor whose context owns the resolver. 333   @param ex The executor whose context owns the resolver.
334   */ 334   */
335   template<class Ex> 335   template<class Ex>
336   requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) && 336   requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
337   capy::Executor<Ex> 337   capy::Executor<Ex>
HITCBC 338   1 explicit resolver(Ex const& ex) : resolver(ex.context()) 338   1 explicit resolver(Ex const& ex) : resolver(ex.context())
339   { 339   {
HITCBC 340   1 } 340   1 }
341   341  
342   /** Move constructor. 342   /** Move constructor.
343   343  
344   Transfers ownership of the resolver resources. After the move, 344   Transfers ownership of the resolver resources. After the move,
345   @p other is in a moved-from state and may only be destroyed or 345   @p other is in a moved-from state and may only be destroyed or
346   assigned to. 346   assigned to.
347   347  
348   @param other The resolver to move from. 348   @param other The resolver to move from.
349   349  
350   @pre No awaitables returned by @p other's `resolve` methods 350   @pre No awaitables returned by @p other's `resolve` methods
351   exist. 351   exist.
352   @pre The execution context associated with @p other must 352   @pre The execution context associated with @p other must
353   outlive this resolver. 353   outlive this resolver.
354   */ 354   */
HITCBC 355   10 resolver(resolver&& other) noexcept : io_object(std::move(other)) {} 355   10 resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
356   356  
357   /** Move assignment operator. 357   /** Move assignment operator.
358   358  
359   Destroys the current implementation and transfers ownership 359   Destroys the current implementation and transfers ownership
360   from @p other. After the move, @p other is in a moved-from 360   from @p other. After the move, @p other is in a moved-from
361   state and may only be destroyed or assigned to. 361   state and may only be destroyed or assigned to.
362   362  
363   @param other The resolver to move from. 363   @param other The resolver to move from.
364   364  
365   @pre No awaitables returned by either `*this` or @p other's 365   @pre No awaitables returned by either `*this` or @p other's
366   `resolve` methods exist. 366   `resolve` methods exist.
367   @pre The execution context associated with @p other must 367   @pre The execution context associated with @p other must
368   outlive this resolver. 368   outlive this resolver.
369   369  
370   @return Reference to this resolver. 370   @return Reference to this resolver.
371   */ 371   */
HITCBC 372   2 resolver& operator=(resolver&& other) noexcept 372   2 resolver& operator=(resolver&& other) noexcept
373   { 373   {
HITCBC 374   2 if (this != &other) 374   2 if (this != &other)
HITCBC 375   2 h_ = std::move(other.h_); 375   2 h_ = std::move(other.h_);
HITCBC 376   2 return *this; 376   2 return *this;
377   } 377   }
378   378  
379   /// Copy construction is disabled; the handle is uniquely owned. 379   /// Copy construction is disabled; the handle is uniquely owned.
380   resolver(resolver const&) = delete; 380   resolver(resolver const&) = delete;
381   /// Copy assignment is disabled; the handle is uniquely owned. 381   /// Copy assignment is disabled; the handle is uniquely owned.
382   resolver& operator=(resolver const&) = delete; 382   resolver& operator=(resolver const&) = delete;
383   383  
384   /** Initiate an asynchronous resolve operation. 384   /** Initiate an asynchronous resolve operation.
385   385  
386   Resolves the host and service names into a list of endpoints. 386   Resolves the host and service names into a list of endpoints.
387   387  
388   This resolver must outlive the returned awaitable. 388   This resolver must outlive the returned awaitable.
389   389  
390   @param host A string identifying a location. May be a descriptive 390   @param host A string identifying a location. May be a descriptive
391   name or a numeric address string. 391   name or a numeric address string.
392   392  
393   @param service A string identifying the requested service. This may 393   @param service A string identifying the requested service. This may
394   be a descriptive name or a numeric string corresponding to a 394   be a descriptive name or a numeric string corresponding to a
395   port number. 395   port number.
396   396  
397   @return An awaitable that completes with 397   @return An awaitable that completes with
398   `results<std::vector<endpoint>>`. 398   `results<std::vector<endpoint>>`.
399   399  
400   @par Example 400   @par Example
401   @par !example forward_resolve 401   @par !example forward_resolve
402   */ 402   */
HITCBC 403   15 [[nodiscard]] auto resolve(std::string_view host, std::string_view service) 403   15 [[nodiscard]] auto resolve(std::string_view host, std::string_view service)
404   { 404   {
HITCBC 405   15 return resolve_awaitable(*this, host, service, resolve_flags::none); 405   15 return resolve_awaitable(*this, host, service, resolve_flags::none);
406   } 406   }
407   407  
408   /** Initiate an asynchronous host-only resolve operation. 408   /** Initiate an asynchronous host-only resolve operation.
409   409  
410   Resolves a host name into its addresses, with no service or 410   Resolves a host name into its addresses, with no service or
411   port involved — the query `getaddrinfo` performs with a null 411   port involved — the query `getaddrinfo` performs with a null
412   service. Use this when the host and port travel separately, 412   service. Use this when the host and port travel separately,
413   as they do in most configuration. 413   as they do in most configuration.
414   414  
415   Each address appears once in the result even when the query 415   Each address appears once in the result even when the query
416   reports it more than once, and link-local results keep 416   reports it more than once, and link-local results keep
417   their zone. 417   their zone.
418   418  
419   @param host The host name or numeric address string. 419   @param host The host name or numeric address string.
420   420  
421   @return An awaitable that completes with 421   @return An awaitable that completes with
422   `results<std::vector<ip_address>>`. 422   `results<std::vector<ip_address>>`.
423   423  
424   @par Example 424   @par Example
425   @par !example host_only_resolve 425   @par !example host_only_resolve
426   */ 426   */
HITCBC 427   3 [[nodiscard]] auto resolve(std::string_view host) 427   3 [[nodiscard]] auto resolve(std::string_view host)
428   { 428   {
HITCBC 429   3 return resolve_host_awaitable(*this, host, resolve_flags::none); 429   3 return resolve_host_awaitable(*this, host, resolve_flags::none);
430   } 430   }
431   431  
432   /** Initiate an asynchronous host-only resolve operation with flags. 432   /** Initiate an asynchronous host-only resolve operation with flags.
433   433  
434   @param host The host name or numeric address string. 434   @param host The host name or numeric address string.
435   @param flags Resolution behavior flags. 435   @param flags Resolution behavior flags.
436   436  
437   @return An awaitable that completes with 437   @return An awaitable that completes with
438   `results<std::vector<ip_address>>`. 438   `results<std::vector<ip_address>>`.
439   */ 439   */
HITCBC 440   3 [[nodiscard]] auto resolve(std::string_view host, resolve_flags flags) 440   3 [[nodiscard]] auto resolve(std::string_view host, resolve_flags flags)
441   { 441   {
HITCBC 442   3 return resolve_host_awaitable(*this, host, flags); 442   3 return resolve_host_awaitable(*this, host, flags);
443   } 443   }
444   444  
445   /** Initiate an asynchronous resolve operation with flags. 445   /** Initiate an asynchronous resolve operation with flags.
446   446  
447   Resolves the host and service names into a list of endpoints. 447   Resolves the host and service names into a list of endpoints.
448   448  
449   This resolver must outlive the returned awaitable. 449   This resolver must outlive the returned awaitable.
450   450  
451   @param host A string identifying a location. 451   @param host A string identifying a location.
452   452  
453   @param service A string identifying the requested service. 453   @param service A string identifying the requested service.
454   454  
455   @param flags Flags controlling resolution behavior. 455   @param flags Flags controlling resolution behavior.
456   456  
457   @return An awaitable that completes with 457   @return An awaitable that completes with
458   `results<std::vector<endpoint>>`. 458   `results<std::vector<endpoint>>`.
459   */ 459   */
HITCBC 460   22 [[nodiscard]] auto resolve( 460   22 [[nodiscard]] auto resolve(
461   std::string_view host, std::string_view service, resolve_flags flags) 461   std::string_view host, std::string_view service, resolve_flags flags)
462   { 462   {
HITCBC 463   22 return resolve_awaitable(*this, host, service, flags); 463   22 return resolve_awaitable(*this, host, service, flags);
464   } 464   }
465   465  
466   /** Initiate an asynchronous reverse resolve operation. 466   /** Initiate an asynchronous reverse resolve operation.
467   467  
468   Resolves an endpoint into a hostname and service name using 468   Resolves an endpoint into a hostname and service name using
469   reverse DNS lookup (PTR record query). 469   reverse DNS lookup (PTR record query).
470   470  
471   This resolver must outlive the returned awaitable. 471   This resolver must outlive the returned awaitable.
472   472  
473   @param ep The endpoint to resolve. 473   @param ep The endpoint to resolve.
474   474  
475   @return An awaitable that completes with 475   @return An awaitable that completes with
476   `results<endpoint_name>`. 476   `results<endpoint_name>`.
477   477  
478   @par Example 478   @par Example
479   @par !example reverse_resolve 479   @par !example reverse_resolve
480   */ 480   */
HITCBC 481   11 [[nodiscard]] auto resolve(endpoint const& ep) 481   11 [[nodiscard]] auto resolve(endpoint const& ep)
482   { 482   {
HITCBC 483   11 return reverse_resolve_awaitable(*this, ep, reverse_flags::none); 483   11 return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
484   } 484   }
485   485  
486   /** Initiate an asynchronous reverse resolve operation with flags. 486   /** Initiate an asynchronous reverse resolve operation with flags.
487   487  
488   Resolves an endpoint into a hostname and service name using 488   Resolves an endpoint into a hostname and service name using
489   reverse DNS lookup (PTR record query). 489   reverse DNS lookup (PTR record query).
490   490  
491   This resolver must outlive the returned awaitable. 491   This resolver must outlive the returned awaitable.
492   492  
493   @param ep The endpoint to resolve. 493   @param ep The endpoint to resolve.
494   494  
495   @param flags Flags controlling resolution behavior. See reverse_flags. 495   @param flags Flags controlling resolution behavior. See reverse_flags.
496   496  
497   @return An awaitable that completes with 497   @return An awaitable that completes with
498   `results<endpoint_name>`. 498   `results<endpoint_name>`.
499   */ 499   */
HITCBC 500   11 [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags) 500   11 [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags)
501   { 501   {
HITCBC 502   11 return reverse_resolve_awaitable(*this, ep, flags); 502   11 return reverse_resolve_awaitable(*this, ep, flags);
503   } 503   }
504   504  
505   /** Cancel any pending asynchronous operations. 505   /** Cancel any pending asynchronous operations.
506   506  
507   A resolve transfers no bytes, so a cancellation always wins. An 507   A resolve transfers no bytes, so a cancellation always wins. An
508   operation reports `errc::operation_canceled` even when the lookup 508   operation reports `errc::operation_canceled` even when the lookup
509   had already completed when the cancellation landed. Check 509   had already completed when the cancellation landed. Check
510   `ec == cond::canceled` for a portable comparison. 510   `ec == cond::canceled` for a portable comparison.
511   */ 511   */
512   void cancel() noexcept; 512   void cancel() noexcept;
513   513  
514   public: 514   public:
515   /** Define backend hooks for DNS resolution operations. 515   /** Define backend hooks for DNS resolution operations.
516   516  
517   Platform backends derive from this to implement forward and 517   Platform backends derive from this to implement forward and
518   reverse DNS resolution via `getaddrinfo`/`getnameinfo`. 518   reverse DNS resolution via `getaddrinfo`/`getnameinfo`.
519   */ 519   */
520   struct implementation : io_object::implementation 520   struct implementation : io_object::implementation
521   { 521   {
522   /** Initiate an asynchronous forward DNS resolution. 522   /** Initiate an asynchronous forward DNS resolution.
523   523  
524   @param cont Continuation to resume on completion; it lives in 524   @param cont Continuation to resume on completion; it lives in
525   the awaiting frame until then. 525   the awaiting frame until then.
526   @param ex Executor for dispatching the completion. 526   @param ex Executor for dispatching the completion.
527   @param host The host name or address literal to resolve. 527   @param host The host name or address literal to resolve.
528   @param service The service name or port number. 528   @param service The service name or port number.
529   @param flags Flags controlling the lookup. 529   @param flags Flags controlling the lookup.
530   @param token Stop token for cancellation. 530   @param token Stop token for cancellation.
531   @param ec Output error code. 531   @param ec Output error code.
532   @param results Output resolver results. 532   @param results Output resolver results.
533   533  
534   @return Coroutine handle to resume immediately. 534   @return Coroutine handle to resume immediately.
535   */ 535   */
536   virtual std::coroutine_handle<> resolve( 536   virtual std::coroutine_handle<> resolve(
537   capy::continuation& cont, 537   capy::continuation& cont,
538   capy::executor_ref ex, 538   capy::executor_ref ex,
539   std::string_view host, 539   std::string_view host,
540   std::string_view service, 540   std::string_view service,
541   resolve_flags flags, 541   resolve_flags flags,
542   std::stop_token token, 542   std::stop_token token,
543   std::error_code* ec, 543   std::error_code* ec,
544   std::vector<endpoint>* results) = 0; 544   std::vector<endpoint>* results) = 0;
545   545  
546   /** Initiate an asynchronous reverse DNS resolution. 546   /** Initiate an asynchronous reverse DNS resolution.
547   547  
548   @param cont Continuation to resume on completion; it lives in 548   @param cont Continuation to resume on completion; it lives in
549   the awaiting frame until then. 549   the awaiting frame until then.
550   @param ex Executor for dispatching the completion. 550   @param ex Executor for dispatching the completion.
551   @param ep The endpoint to resolve. 551   @param ep The endpoint to resolve.
552   @param flags Flags controlling the lookup. 552   @param flags Flags controlling the lookup.
553   @param token Stop token for cancellation. 553   @param token Stop token for cancellation.
554   @param ec Output error code. 554   @param ec Output error code.
555   @param result Output reverse-resolution result. 555   @param result Output reverse-resolution result.
556   556  
557   @return Coroutine handle to resume immediately. 557   @return Coroutine handle to resume immediately.
558   */ 558   */
559   virtual std::coroutine_handle<> reverse_resolve( 559   virtual std::coroutine_handle<> reverse_resolve(
560   capy::continuation& cont, 560   capy::continuation& cont,
561   capy::executor_ref ex, 561   capy::executor_ref ex,
562   endpoint const& ep, 562   endpoint const& ep,
563   reverse_flags flags, 563   reverse_flags flags,
564   std::stop_token token, 564   std::stop_token token,
565   std::error_code* ec, 565   std::error_code* ec,
566   endpoint_name* result) = 0; 566   endpoint_name* result) = 0;
567   567  
568   /// Cancel pending resolve operations. 568   /// Cancel pending resolve operations.
569   virtual void cancel() noexcept = 0; 569   virtual void cancel() noexcept = 0;
570   }; 570   };
571   571  
572   protected: 572   protected:
573   /** Adopt an existing handle. 573   /** Adopt an existing handle.
574   574  
575   @param h The handle the resolver takes ownership of. 575   @param h The handle the resolver takes ownership of.
576   */ 576   */
577   explicit resolver(handle h) noexcept : io_object(std::move(h)) {} 577   explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
578   578  
579   private: 579   private:
HITCBC 580   69 inline implementation& get() const noexcept 580   69 inline implementation& get() const noexcept
581   { 581   {
HITCBC 582   69 return *static_cast<implementation*>(h_.get()); 582   69 return *static_cast<implementation*>(h_.get());
583   } 583   }
584   }; 584   };
585   585  
586   } // namespace boost::corosio 586   } // namespace boost::corosio
587   587  
588   #endif 588   #endif