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