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. It resumes the awaiting coroutine through that coroutine's 180   protocol. It resumes the awaiting coroutine through that coroutine's
181   executor, not through the executor of the context that owns the 181   executor, not through the executor of the context that owns the
182   resolver. 182   resolver.
183   183  
184   @par Thread Safety 184   @par Thread Safety
185   Distinct objects: Safe.@n 185   Distinct objects: Safe.@n
186   Shared objects: Unsafe. A resolver must not have concurrent resolve 186   Shared objects: Unsafe. A resolver must not have concurrent resolve
187   operations. 187   operations.
188   188  
189   @par Semantics 189   @par Semantics
190   Wraps platform DNS resolution (`getaddrinfo`/`getnameinfo`). 190   Wraps platform DNS resolution (`getaddrinfo`/`getnameinfo`).
191   Operations dispatch to OS resolver APIs via the `io_context` 191   Operations dispatch to OS resolver APIs via the `io_context`
192   thread pool. 192   thread pool.
193   193  
194   @par Example 194   @par Example
195   @par !example resolver 195   @par !example resolver
196   */ 196   */
197   class BOOST_COROSIO_DECL resolver : public io_object 197   class BOOST_COROSIO_DECL resolver : public io_object
198   { 198   {
199   struct resolve_awaitable 199   struct resolve_awaitable
200   : detail::value_op_base<resolve_awaitable, std::vector<endpoint>> 200   : detail::value_op_base<resolve_awaitable, std::vector<endpoint>>
201   { 201   {
202   private: 202   private:
203   friend resolver; 203   friend resolver;
204   204  
HITCBC 205   37 resolve_awaitable( 205   37 resolve_awaitable(
206   resolver& r, 206   resolver& r,
207   std::string_view host, 207   std::string_view host,
208   std::string_view service, 208   std::string_view service,
209   resolve_flags flags) noexcept 209   resolve_flags flags) noexcept
HITCBC 210   74 : r_(r) 210   74 : r_(r)
HITCBC 211   74 , host_(host) 211   74 , host_(host)
HITCBC 212   74 , service_(service) 212   74 , service_(service)
HITCBC 213   37 , flags_(flags) 213   37 , flags_(flags)
214   { 214   {
HITCBC 215   37 } 215   37 }
216   216  
217   friend detail::value_op_base<resolve_awaitable, std::vector<endpoint>>; 217   friend detail::value_op_base<resolve_awaitable, std::vector<endpoint>>;
218   resolver& r_; 218   resolver& r_;
219   std::string host_; 219   std::string host_;
220   std::string service_; 220   std::string service_;
221   resolve_flags flags_; 221   resolve_flags flags_;
222   222  
223   std::coroutine_handle<> 223   std::coroutine_handle<>
HITCBC 224   36 dispatch(capy::continuation& cont, capy::executor_ref ex) const 224   36 dispatch(capy::continuation& cont, capy::executor_ref ex) const
225   { 225   {
HITCBC 226   108 return r_.get().resolve( 226   108 return r_.get().resolve(
HITCBC 227   108 cont, ex, host_, service_, flags_, token_, &ec_, &value_); 227   108 cont, ex, host_, service_, flags_, token_, &ec_, &value_);
228   } 228   }
229   }; 229   };
230   230  
231   struct resolve_host_awaitable 231   struct resolve_host_awaitable
232   : detail::value_op_base<resolve_host_awaitable, std::vector<endpoint>> 232   : detail::value_op_base<resolve_host_awaitable, std::vector<endpoint>>
233   { 233   {
234   private: 234   private:
235   friend resolver; 235   friend resolver;
236   236  
HITCBC 237   6 resolve_host_awaitable( 237   6 resolve_host_awaitable(
238   resolver& r, std::string_view host, resolve_flags flags) noexcept 238   resolver& r, std::string_view host, resolve_flags flags) noexcept
HITCBC 239   12 : r_(r) 239   12 : r_(r)
HITCBC 240   12 , host_(host) 240   12 , host_(host)
HITCBC 241   6 , flags_(flags) 241   6 , flags_(flags)
242   { 242   {
HITCBC 243   6 } 243   6 }
244   244  
245   friend detail:: 245   friend detail::
246   value_op_base<resolve_host_awaitable, std::vector<endpoint>>; 246   value_op_base<resolve_host_awaitable, std::vector<endpoint>>;
247   resolver& r_; 247   resolver& r_;
248   std::string host_; 248   std::string host_;
249   resolve_flags flags_; 249   resolve_flags flags_;
250   250  
251   std::coroutine_handle<> 251   std::coroutine_handle<>
HITCBC 252   5 dispatch(capy::continuation& cont, capy::executor_ref ex) const 252   5 dispatch(capy::continuation& cont, capy::executor_ref ex) const
253   { 253   {
254   // An empty service reaches the system resolver as null, 254   // An empty service reaches the system resolver as null,
255   // which is the host-only query 255   // which is the host-only query
HITCBC 256   15 return r_.get().resolve( 256   15 return r_.get().resolve(
HITCBC 257   15 cont, ex, host_, {}, flags_, token_, &ec_, &value_); 257   15 cont, ex, host_, {}, flags_, token_, &ec_, &value_);
258   } 258   }
259   259  
260   public: 260   public:
261   // Shadows the base: the endpoint result is reshaped into 261   // Shadows the base: the endpoint result is reshaped into
262   // the honest address list 262   // the honest address list
263   [[nodiscard]] capy::results<std::vector<ip_address>> 263   [[nodiscard]] capy::results<std::vector<ip_address>>
HITCBC 264   6 await_resume() const 264   6 await_resume() const
265   { 265   {
HITCBC 266   6 std::vector<ip_address> addrs; 266   6 std::vector<ip_address> addrs;
HITCBC 267   6 addrs.reserve(value_.size()); 267   6 addrs.reserve(value_.size());
HITCBC 268   9 for (auto const& entry : value_) 268   9 for (auto const& entry : value_)
269   { 269   {
HITCBC 270   3 auto a = entry.address(); 270   3 auto a = entry.address();
HITCBC 271   3 bool duplicate = false; 271   3 bool duplicate = false;
HITCBC 272   3 for (auto const& seen : addrs) 272   3 for (auto const& seen : addrs)
273   { 273   {
MISUBC 274   ✗ if (seen == a) 274   ✗ if (seen == a)
275   { 275   {
MISUBC 276   ✗ duplicate = true; 276   ✗ duplicate = true;
MISUBC 277   ✗ break; 277   ✗ break;
278   } 278   }
279   } 279   }
280   // The same address can come back more than once 280   // The same address can come back more than once
281   // (mixed name sources, repeated records); each 281   // (mixed name sources, repeated records); each
282   // address is reported once 282   // address is reported once
HITCBC 283   3 if (!duplicate) 283   3 if (!duplicate)
HITCBC 284   3 addrs.push_back(a); 284   3 addrs.push_back(a);
285   } 285   }
HITCBC 286   12 return {ec_, std::move(addrs)}; 286   12 return {ec_, std::move(addrs)};
HITCBC 287   6 } 287   6 }
288   }; 288   };
289   289  
290   struct reverse_resolve_awaitable 290   struct reverse_resolve_awaitable
291   : detail::value_op_base<reverse_resolve_awaitable, endpoint_name> 291   : detail::value_op_base<reverse_resolve_awaitable, endpoint_name>
292   { 292   {
293   private: 293   private:
294   friend resolver; 294   friend resolver;
295   295  
HITCBC 296   22 reverse_resolve_awaitable( 296   22 reverse_resolve_awaitable(
297   resolver& r, endpoint const& ep, reverse_flags flags) noexcept 297   resolver& r, endpoint const& ep, reverse_flags flags) noexcept
HITCBC 298   44 : r_(r) 298   44 : r_(r)
HITCBC 299   22 , ep_(ep) 299   22 , ep_(ep)
HITCBC 300   22 , flags_(flags) 300   22 , flags_(flags)
301   { 301   {
HITCBC 302   22 } 302   22 }
303   303  
304   friend detail::value_op_base<reverse_resolve_awaitable, endpoint_name>; 304   friend detail::value_op_base<reverse_resolve_awaitable, endpoint_name>;
305   305  
306   resolver& r_; 306   resolver& r_;
307   endpoint ep_; 307   endpoint ep_;
308   reverse_flags flags_; 308   reverse_flags flags_;
309   309  
310   std::coroutine_handle<> 310   std::coroutine_handle<>
HITCBC 311   21 dispatch(capy::continuation& cont, capy::executor_ref ex) const 311   21 dispatch(capy::continuation& cont, capy::executor_ref ex) const
312   { 312   {
HITCBC 313   42 return r_.get().reverse_resolve( 313   42 return r_.get().reverse_resolve(
HITCBC 314   42 cont, ex, ep_, flags_, token_, &ec_, &value_); 314   42 cont, ex, ep_, flags_, token_, &ec_, &value_);
315   } 315   }
316   }; 316   };
317   317  
318   public: 318   public:
319   /** Destructor. 319   /** Destructor.
320   320  
321   Cancels any pending operations. 321   Cancels any pending operations.
322   */ 322   */
323   ~resolver() override; 323   ~resolver() override;
324   324  
325   /** Construct a resolver from an execution context. 325   /** Construct a resolver from an execution context.
326   326  
327   @param ctx The execution context that owns this resolver. 327   @param ctx The execution context that owns this resolver.
328   */ 328   */
329   explicit resolver(capy::execution_context& ctx); 329   explicit resolver(capy::execution_context& ctx);
330   330  
331   /** Construct a resolver from an executor. 331   /** Construct a resolver from an executor.
332   332  
333   The resolver is associated with the executor's context. 333   The resolver is associated with the executor's context.
334   334  
335   @param ex The executor whose context owns the resolver. 335   @param ex The executor whose context owns the resolver.
336   */ 336   */
337   template<class Ex> 337   template<class Ex>
338   requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) && 338   requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
339   capy::Executor<Ex> 339   capy::Executor<Ex>
HITCBC 340   1 explicit resolver(Ex const& ex) : resolver(ex.context()) 340   1 explicit resolver(Ex const& ex) : resolver(ex.context())
341   { 341   {
HITCBC 342   1 } 342   1 }
343   343  
344   /** Move constructor. 344   /** Move constructor.
345   345  
346   Transfers ownership of the resolver resources. After the move, 346   Transfers ownership of the resolver resources. After the move,
347   @p other is in a moved-from state and may only be destroyed or 347   @p other is in a moved-from state and may only be destroyed or
348   assigned to. 348   assigned to.
349   349  
350   @param other The resolver to move from. 350   @param other The resolver to move from.
351   351  
352   @pre No awaitables returned by @p other's `resolve` methods 352   @pre No awaitables returned by @p other's `resolve` methods
353   exist. 353   exist.
354   @pre The execution context associated with @p other must 354   @pre The execution context associated with @p other must
355   outlive this resolver. 355   outlive this resolver.
356   */ 356   */
HITCBC 357   10 resolver(resolver&& other) noexcept : io_object(std::move(other)) {} 357   10 resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
358   358  
359   /** Move assignment operator. 359   /** Move assignment operator.
360   360  
361   Destroys the current implementation and transfers ownership 361   Destroys the current implementation and transfers ownership
362   from @p other. After the move, @p other is in a moved-from 362   from @p other. After the move, @p other is in a moved-from
363   state and may only be destroyed or assigned to. 363   state and may only be destroyed or assigned to.
364   364  
365   @param other The resolver to move from. 365   @param other The resolver to move from.
366   366  
367   @pre No awaitables returned by either `*this` or @p other's 367   @pre No awaitables returned by either `*this` or @p other's
368   `resolve` methods exist. 368   `resolve` methods exist.
369   @pre The execution context associated with @p other must 369   @pre The execution context associated with @p other must
370   outlive this resolver. 370   outlive this resolver.
371   371  
372   @return Reference to this resolver. 372   @return Reference to this resolver.
373   */ 373   */
HITCBC 374   2 resolver& operator=(resolver&& other) noexcept 374   2 resolver& operator=(resolver&& other) noexcept
375   { 375   {
HITCBC 376   2 if (this != &other) 376   2 if (this != &other)
HITCBC 377   2 h_ = std::move(other.h_); 377   2 h_ = std::move(other.h_);
HITCBC 378   2 return *this; 378   2 return *this;
379   } 379   }
380   380  
381   /// Copy construction is disabled; the handle is uniquely owned. 381   /// Copy construction is disabled; the handle is uniquely owned.
382   resolver(resolver const&) = delete; 382   resolver(resolver const&) = delete;
383   /// Copy assignment is disabled; the handle is uniquely owned. 383   /// Copy assignment is disabled; the handle is uniquely owned.
384   resolver& operator=(resolver const&) = delete; 384   resolver& operator=(resolver const&) = delete;
385   385  
386   /** Initiate an asynchronous resolve operation. 386   /** Initiate an asynchronous resolve operation.
387   387  
388   Resolves the host and service names into a list of endpoints. 388   Resolves the host and service names into a list of endpoints.
389   389  
390   This resolver must outlive the returned awaitable. 390   This resolver must outlive the returned awaitable.
391   391  
392   @param host A string identifying a location. May be a descriptive 392   @param host A string identifying a location. May be a descriptive
393   name or a numeric address string. 393   name or a numeric address string.
394   394  
395   @param service A string identifying the requested service. This may 395   @param service A string identifying the requested service. This may
396   be a descriptive name or a numeric string corresponding to a 396   be a descriptive name or a numeric string corresponding to a
397   port number. 397   port number.
398   398  
399   @return An awaitable that completes with 399   @return An awaitable that completes with
400   `results<std::vector<endpoint>>`. 400   `results<std::vector<endpoint>>`.
401   401  
402   @par Example 402   @par Example
403   @par !example forward_resolve 403   @par !example forward_resolve
404   */ 404   */
HITCBC 405   15 [[nodiscard]] auto resolve(std::string_view host, std::string_view service) 405   15 [[nodiscard]] auto resolve(std::string_view host, std::string_view service)
406   { 406   {
HITCBC 407   15 return resolve_awaitable(*this, host, service, resolve_flags::none); 407   15 return resolve_awaitable(*this, host, service, resolve_flags::none);
408   } 408   }
409   409  
410   /** Initiate an asynchronous host-only resolve operation. 410   /** Initiate an asynchronous host-only resolve operation.
411   411  
412   Resolves a host name into its addresses, with no service or 412   Resolves a host name into its addresses, with no service or
413   port involved — the query `getaddrinfo` performs with a null 413   port involved — the query `getaddrinfo` performs with a null
414   service. Use this when the host and port travel separately, 414   service. Use this when the host and port travel separately,
415   as they do in most configuration. 415   as they do in most configuration.
416   416  
417   Each address appears once in the result even when the query 417   Each address appears once in the result even when the query
418   reports it more than once, and link-local results keep 418   reports it more than once, and link-local results keep
419   their zone. 419   their zone.
420   420  
421   @param host The host name or numeric address string. 421   @param host The host name or numeric address string.
422   422  
423   @return An awaitable that completes with 423   @return An awaitable that completes with
424   `results<std::vector<ip_address>>`. 424   `results<std::vector<ip_address>>`.
425   425  
426   @par Example 426   @par Example
427   @par !example host_only_resolve 427   @par !example host_only_resolve
428   */ 428   */
HITCBC 429   3 [[nodiscard]] auto resolve(std::string_view host) 429   3 [[nodiscard]] auto resolve(std::string_view host)
430   { 430   {
HITCBC 431   3 return resolve_host_awaitable(*this, host, resolve_flags::none); 431   3 return resolve_host_awaitable(*this, host, resolve_flags::none);
432   } 432   }
433   433  
434   /** Initiate an asynchronous host-only resolve operation with flags. 434   /** Initiate an asynchronous host-only resolve operation with flags.
435   435  
436   @param host The host name or numeric address string. 436   @param host The host name or numeric address string.
437   @param flags Resolution behavior flags. 437   @param flags Resolution behavior flags.
438   438  
439   @return An awaitable that completes with 439   @return An awaitable that completes with
440   `results<std::vector<ip_address>>`. 440   `results<std::vector<ip_address>>`.
441   */ 441   */
HITCBC 442   3 [[nodiscard]] auto resolve(std::string_view host, resolve_flags flags) 442   3 [[nodiscard]] auto resolve(std::string_view host, resolve_flags flags)
443   { 443   {
HITCBC 444   3 return resolve_host_awaitable(*this, host, flags); 444   3 return resolve_host_awaitable(*this, host, flags);
445   } 445   }
446   446  
447   /** Initiate an asynchronous resolve operation with flags. 447   /** Initiate an asynchronous resolve operation with flags.
448   448  
449   Resolves the host and service names into a list of endpoints. 449   Resolves the host and service names into a list of endpoints.
450   450  
451   This resolver must outlive the returned awaitable. 451   This resolver must outlive the returned awaitable.
452   452  
453   @param host A string identifying a location. 453   @param host A string identifying a location.
454   454  
455   @param service A string identifying the requested service. 455   @param service A string identifying the requested service.
456   456  
457   @param flags Flags controlling resolution behavior. 457   @param flags Flags controlling resolution behavior.
458   458  
459   @return An awaitable that completes with 459   @return An awaitable that completes with
460   `results<std::vector<endpoint>>`. 460   `results<std::vector<endpoint>>`.
461   */ 461   */
HITCBC 462   22 [[nodiscard]] auto resolve( 462   22 [[nodiscard]] auto resolve(
463   std::string_view host, std::string_view service, resolve_flags flags) 463   std::string_view host, std::string_view service, resolve_flags flags)
464   { 464   {
HITCBC 465   22 return resolve_awaitable(*this, host, service, flags); 465   22 return resolve_awaitable(*this, host, service, flags);
466   } 466   }
467   467  
468   /** Initiate an asynchronous reverse resolve operation. 468   /** Initiate an asynchronous reverse resolve operation.
469   469  
470   Resolves an endpoint into a hostname and service name using 470   Resolves an endpoint into a hostname and service name using
471   reverse DNS lookup (PTR record query). 471   reverse DNS lookup (PTR record query).
472   472  
473   This resolver must outlive the returned awaitable. 473   This resolver must outlive the returned awaitable.
474   474  
475   @param ep The endpoint to resolve. 475   @param ep The endpoint to resolve.
476   476  
477   @return An awaitable that completes with 477   @return An awaitable that completes with
478   `results<endpoint_name>`. 478   `results<endpoint_name>`.
479   479  
480   @par Example 480   @par Example
481   @par !example reverse_resolve 481   @par !example reverse_resolve
482   */ 482   */
HITCBC 483   11 [[nodiscard]] auto resolve(endpoint const& ep) 483   11 [[nodiscard]] auto resolve(endpoint const& ep)
484   { 484   {
HITCBC 485   11 return reverse_resolve_awaitable(*this, ep, reverse_flags::none); 485   11 return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
486   } 486   }
487   487  
488   /** Initiate an asynchronous reverse resolve operation with flags. 488   /** Initiate an asynchronous reverse resolve operation with flags.
489   489  
490   Resolves an endpoint into a hostname and service name using 490   Resolves an endpoint into a hostname and service name using
491   reverse DNS lookup (PTR record query). 491   reverse DNS lookup (PTR record query).
492   492  
493   This resolver must outlive the returned awaitable. 493   This resolver must outlive the returned awaitable.
494   494  
495   @param ep The endpoint to resolve. 495   @param ep The endpoint to resolve.
496   496  
497   @param flags Flags controlling resolution behavior. See reverse_flags. 497   @param flags Flags controlling resolution behavior. See reverse_flags.
498   498  
499   @return An awaitable that completes with 499   @return An awaitable that completes with
500   `results<endpoint_name>`. 500   `results<endpoint_name>`.
501   */ 501   */
HITCBC 502   11 [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags) 502   11 [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags)
503   { 503   {
HITCBC 504   11 return reverse_resolve_awaitable(*this, ep, flags); 504   11 return reverse_resolve_awaitable(*this, ep, flags);
505   } 505   }
506   506  
507   /** Cancel any pending asynchronous operations. 507   /** Cancel any pending asynchronous operations.
508   508  
509   A resolve transfers no bytes, so a cancellation always wins. An 509   A resolve transfers no bytes, so a cancellation always wins. An
510   operation reports `errc::operation_canceled` even when the lookup 510   operation reports `errc::operation_canceled` even when the lookup
511   had already completed when the cancellation landed. Check 511   had already completed when the cancellation landed. Check
512   `ec == cond::canceled` for a portable comparison. 512   `ec == cond::canceled` for a portable comparison.
513   */ 513   */
514   void cancel() noexcept; 514   void cancel() noexcept;
515   515  
516   public: 516   public:
517   /** Define backend hooks for DNS resolution operations. 517   /** Define backend hooks for DNS resolution operations.
518   518  
519   Platform backends derive from this to implement forward and 519   Platform backends derive from this to implement forward and
520   reverse DNS resolution via `getaddrinfo`/`getnameinfo`. 520   reverse DNS resolution via `getaddrinfo`/`getnameinfo`.
521   */ 521   */
522   struct implementation : io_object::implementation 522   struct implementation : io_object::implementation
523   { 523   {
524   /** Initiate an asynchronous forward DNS resolution. 524   /** Initiate an asynchronous forward DNS resolution.
525   525  
526   @param cont Continuation to resume on completion; it lives in 526   @param cont Continuation to resume on completion; it lives in
527   the awaiting frame until then. 527   the awaiting frame until then.
528   @param ex Executor for dispatching the completion. 528   @param ex Executor for dispatching the completion.
529   @param host The host name or address literal to resolve. 529   @param host The host name or address literal to resolve.
530   @param service The service name or port number. 530   @param service The service name or port number.
531   @param flags Flags controlling the lookup. 531   @param flags Flags controlling the lookup.
532   @param token Stop token for cancellation. 532   @param token Stop token for cancellation.
533   @param ec Output error code. 533   @param ec Output error code.
534   @param results Output resolver results. 534   @param results Output resolver results.
535   535  
536   @return Coroutine handle to resume immediately. 536   @return Coroutine handle to resume immediately.
537   */ 537   */
538   virtual std::coroutine_handle<> resolve( 538   virtual std::coroutine_handle<> resolve(
539   capy::continuation& cont, 539   capy::continuation& cont,
540   capy::executor_ref ex, 540   capy::executor_ref ex,
541   std::string_view host, 541   std::string_view host,
542   std::string_view service, 542   std::string_view service,
543   resolve_flags flags, 543   resolve_flags flags,
544   std::stop_token token, 544   std::stop_token token,
545   std::error_code* ec, 545   std::error_code* ec,
546   std::vector<endpoint>* results) = 0; 546   std::vector<endpoint>* results) = 0;
547   547  
548   /** Initiate an asynchronous reverse DNS resolution. 548   /** Initiate an asynchronous reverse DNS resolution.
549   549  
550   @param cont Continuation to resume on completion; it lives in 550   @param cont Continuation to resume on completion; it lives in
551   the awaiting frame until then. 551   the awaiting frame until then.
552   @param ex Executor for dispatching the completion. 552   @param ex Executor for dispatching the completion.
553   @param ep The endpoint to resolve. 553   @param ep The endpoint to resolve.
554   @param flags Flags controlling the lookup. 554   @param flags Flags controlling the lookup.
555   @param token Stop token for cancellation. 555   @param token Stop token for cancellation.
556   @param ec Output error code. 556   @param ec Output error code.
557   @param result Output reverse-resolution result. 557   @param result Output reverse-resolution result.
558   558  
559   @return Coroutine handle to resume immediately. 559   @return Coroutine handle to resume immediately.
560   */ 560   */
561   virtual std::coroutine_handle<> reverse_resolve( 561   virtual std::coroutine_handle<> reverse_resolve(
562   capy::continuation& cont, 562   capy::continuation& cont,
563   capy::executor_ref ex, 563   capy::executor_ref ex,
564   endpoint const& ep, 564   endpoint const& ep,
565   reverse_flags flags, 565   reverse_flags flags,
566   std::stop_token token, 566   std::stop_token token,
567   std::error_code* ec, 567   std::error_code* ec,
568   endpoint_name* result) = 0; 568   endpoint_name* result) = 0;
569   569  
570   /// Cancel pending resolve operations. 570   /// Cancel pending resolve operations.
571   virtual void cancel() noexcept = 0; 571   virtual void cancel() noexcept = 0;
572   }; 572   };
573   573  
574   protected: 574   protected:
575   /** Adopt an existing handle. 575   /** Adopt an existing handle.
576   576  
577   @param h The handle the resolver takes ownership of. 577   @param h The handle the resolver takes ownership of.
578   */ 578   */
579   explicit resolver(handle h) noexcept : io_object(std::move(h)) {} 579   explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
580   580  
581   private: 581   private:
HITCBC 582   69 inline implementation& get() const noexcept 582   69 inline implementation& get() const noexcept
583   { 583   {
HITCBC 584   69 return *static_cast<implementation*>(h_.get()); 584   69 return *static_cast<implementation*>(h_.get());
585   } 585   }
586   }; 586   };
587   587  
588   } // namespace boost::corosio 588   } // namespace boost::corosio
589   589  
590   #endif 590   #endif