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   /** An asynchronous DNS resolver for coroutine I/O. 174   /** An asynchronous DNS resolver for coroutine I/O.
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   resolver& r_; 198   resolver& r_;
199   std::string host_; 199   std::string host_;
200   std::string service_; 200   std::string service_;
201   resolve_flags flags_; 201   resolve_flags flags_;
202   202  
HITCBC 203   29 resolve_awaitable( 203   29 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   58 : r_(r) 208   58 : r_(r)
HITCBC 209   58 , host_(host) 209   58 , host_(host)
HITCBC 210   58 , service_(service) 210   58 , service_(service)
HITCBC 211   29 , flags_(flags) 211   29 , flags_(flags)
212   { 212   {
HITCBC 213   29 } 213   29 }
214   214  
215   std::coroutine_handle<> 215   std::coroutine_handle<>
HITCBC 216   28 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 216   28 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
217   { 217   {
HITCBC 218   84 return r_.get().resolve( 218   84 return r_.get().resolve(
HITCBC 219   84 h, ex, host_, service_, flags_, token_, &ec_, &value_); 219   84 h, ex, host_, service_, flags_, token_, &ec_, &value_);
220   } 220   }
221   }; 221   };
222   222  
223   struct resolve_host_awaitable 223   struct resolve_host_awaitable
224   : detail::value_op_base<resolve_host_awaitable, std::vector<endpoint>> 224   : detail::value_op_base<resolve_host_awaitable, std::vector<endpoint>>
225   { 225   {
226   resolver& r_; 226   resolver& r_;
227   std::string host_; 227   std::string host_;
228   resolve_flags flags_; 228   resolve_flags flags_;
229   229  
HITCBC 230   6 resolve_host_awaitable( 230   6 resolve_host_awaitable(
231   resolver& r, std::string_view host, resolve_flags flags) noexcept 231   resolver& r, std::string_view host, resolve_flags flags) noexcept
HITCBC 232   12 : r_(r) 232   12 : r_(r)
HITCBC 233   12 , host_(host) 233   12 , host_(host)
HITCBC 234   6 , flags_(flags) 234   6 , flags_(flags)
235   { 235   {
HITCBC 236   6 } 236   6 }
237   237  
238   std::coroutine_handle<> 238   std::coroutine_handle<>
HITCBC 239   5 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 239   5 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
240   { 240   {
241   // An empty service reaches the system resolver as null, 241   // An empty service reaches the system resolver as null,
242   // which is the host-only query 242   // which is the host-only query
HITCBC 243   15 return r_.get().resolve( 243   15 return r_.get().resolve(
HITCBC 244   15 h, ex, host_, {}, flags_, token_, &ec_, &value_); 244   15 h, ex, host_, {}, flags_, token_, &ec_, &value_);
245   } 245   }
246   246  
247   // Shadows the base: the endpoint result is reshaped into 247   // Shadows the base: the endpoint result is reshaped into
248   // the honest address list 248   // the honest address list
249   [[nodiscard]] capy::io_result<std::vector<ip_address>> 249   [[nodiscard]] capy::io_result<std::vector<ip_address>>
HITCBC 250   6 await_resume() const 250   6 await_resume() const
251   { 251   {
HITCBC 252   6 std::vector<ip_address> addrs; 252   6 std::vector<ip_address> addrs;
HITCBC 253   6 addrs.reserve(value_.size()); 253   6 addrs.reserve(value_.size());
HITCBC 254   9 for (auto const& entry : value_) 254   9 for (auto const& entry : value_)
255   { 255   {
HITCBC 256   3 auto a = entry.address(); 256   3 auto a = entry.address();
HITCBC 257   3 bool duplicate = false; 257   3 bool duplicate = false;
HITCBC 258   3 for (auto const& seen : addrs) 258   3 for (auto const& seen : addrs)
259   { 259   {
MISUBC 260   ✗ if (seen == a) 260   ✗ if (seen == a)
261   { 261   {
MISUBC 262   ✗ duplicate = true; 262   ✗ duplicate = true;
MISUBC 263   ✗ break; 263   ✗ break;
264   } 264   }
265   } 265   }
266   // The same address can come back more than once 266   // The same address can come back more than once
267   // (mixed name sources, repeated records); each 267   // (mixed name sources, repeated records); each
268   // address is reported once 268   // address is reported once
HITCBC 269   3 if (!duplicate) 269   3 if (!duplicate)
HITCBC 270   3 addrs.push_back(a); 270   3 addrs.push_back(a);
271   } 271   }
HITCBC 272   12 return {ec_, std::move(addrs)}; 272   12 return {ec_, std::move(addrs)};
HITCBC 273   6 } 273   6 }
274   }; 274   };
275   275  
276   struct reverse_resolve_awaitable 276   struct reverse_resolve_awaitable
277   : detail::value_op_base<reverse_resolve_awaitable, endpoint_name> 277   : detail::value_op_base<reverse_resolve_awaitable, endpoint_name>
278   { 278   {
279   resolver& r_; 279   resolver& r_;
280   endpoint ep_; 280   endpoint ep_;
281   reverse_flags flags_; 281   reverse_flags flags_;
282   282  
HITCBC 283   20 reverse_resolve_awaitable( 283   20 reverse_resolve_awaitable(
284   resolver& r, endpoint const& ep, reverse_flags flags) noexcept 284   resolver& r, endpoint const& ep, reverse_flags flags) noexcept
HITCBC 285   40 : r_(r) 285   40 : r_(r)
HITCBC 286   20 , ep_(ep) 286   20 , ep_(ep)
HITCBC 287   20 , flags_(flags) 287   20 , flags_(flags)
288   { 288   {
HITCBC 289   20 } 289   20 }
290   290  
291   std::coroutine_handle<> 291   std::coroutine_handle<>
HITCBC 292   19 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 292   19 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
293   { 293   {
HITCBC 294   38 return r_.get().reverse_resolve( 294   38 return r_.get().reverse_resolve(
HITCBC 295   38 h, ex, ep_, flags_, token_, &ec_, &value_); 295   38 h, ex, ep_, flags_, token_, &ec_, &value_);
296   } 296   }
297   }; 297   };
298   298  
299   public: 299   public:
300   /** Destructor. 300   /** Destructor.
301   301  
302   Cancels any pending operations. 302   Cancels any pending operations.
303   */ 303   */
304   ~resolver() override; 304   ~resolver() override;
305   305  
306   /** Construct a resolver from an execution context. 306   /** Construct a resolver from an execution context.
307   307  
308   @param ctx The execution context that will own this resolver. 308   @param ctx The execution context that will own this resolver.
309   */ 309   */
310   explicit resolver(capy::execution_context& ctx); 310   explicit resolver(capy::execution_context& ctx);
311   311  
312   /** Construct a resolver from an executor. 312   /** Construct a resolver from an executor.
313   313  
314   The resolver is associated with the executor's context. 314   The resolver is associated with the executor's context.
315   315  
316   @param ex The executor whose context will own the resolver. 316   @param ex The executor whose context will own the resolver.
317   */ 317   */
318   template<class Ex> 318   template<class Ex>
319   requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) && 319   requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
320   capy::Executor<Ex> 320   capy::Executor<Ex>
HITCBC 321   1 explicit resolver(Ex const& ex) : resolver(ex.context()) 321   1 explicit resolver(Ex const& ex) : resolver(ex.context())
322   { 322   {
HITCBC 323   1 } 323   1 }
324   324  
325   /** Move constructor. 325   /** Move constructor.
326   326  
327   Transfers ownership of the resolver resources. After the move, 327   Transfers ownership of the resolver resources. After the move,
328   @p other is in a moved-from state and may only be destroyed or 328   @p other is in a moved-from state and may only be destroyed or
329   assigned to. 329   assigned to.
330   330  
331   @param other The resolver to move from. 331   @param other The resolver to move from.
332   332  
333   @pre No awaitables returned by @p other's `resolve` methods 333   @pre No awaitables returned by @p other's `resolve` methods
334   exist. 334   exist.
335   @pre The execution context associated with @p other must 335   @pre The execution context associated with @p other must
336   outlive this resolver. 336   outlive this resolver.
337   */ 337   */
HITCBC 338   2 resolver(resolver&& other) noexcept : io_object(std::move(other)) {} 338   2 resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
339   339  
340   /** Move assignment operator. 340   /** Move assignment operator.
341   341  
342   Destroys the current implementation and transfers ownership 342   Destroys the current implementation and transfers ownership
343   from @p other. After the move, @p other is in a moved-from 343   from @p other. After the move, @p other is in a moved-from
344   state and may only be destroyed or assigned to. 344   state and may only be destroyed or 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 either `*this` or @p other's 348   @pre No awaitables returned by either `*this` or @p other's
349   `resolve` methods exist. 349   `resolve` methods 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  
353   @return Reference to this resolver. 353   @return Reference to this resolver.
354   */ 354   */
HITCBC 355   2 resolver& operator=(resolver&& other) noexcept 355   2 resolver& operator=(resolver&& other) noexcept
356   { 356   {
HITCBC 357   2 if (this != &other) 357   2 if (this != &other)
HITCBC 358   2 h_ = std::move(other.h_); 358   2 h_ = std::move(other.h_);
HITCBC 359   2 return *this; 359   2 return *this;
360   } 360   }
361   361  
362   resolver(resolver const&) = delete; 362   resolver(resolver const&) = delete;
363   resolver& operator=(resolver const&) = delete; 363   resolver& operator=(resolver const&) = delete;
364   364  
365   /** Initiate an asynchronous resolve operation. 365   /** Initiate an asynchronous resolve operation.
366   366  
367   Resolves the host and service names into a list of endpoints. 367   Resolves the host and service names into a list of endpoints.
368   368  
369   This resolver must outlive the returned awaitable. 369   This resolver must outlive the returned awaitable.
370   370  
371   @param host A string identifying a location. May be a descriptive 371   @param host A string identifying a location. May be a descriptive
372   name or a numeric address string. 372   name or a numeric address string.
373   373  
374   @param service A string identifying the requested service. This may 374   @param service A string identifying the requested service. This may
375   be a descriptive name or a numeric string corresponding to a 375   be a descriptive name or a numeric string corresponding to a
376   port number. 376   port number.
377   377  
378   @return An awaitable that completes with 378   @return An awaitable that completes with
379   `io_result<std::vector<endpoint>>`. 379   `io_result<std::vector<endpoint>>`.
380   380  
381   @par Example 381   @par Example
382   @par !example forward_resolve 382   @par !example forward_resolve
383   */ 383   */
HITCBC 384   13 [[nodiscard]] auto resolve(std::string_view host, std::string_view service) 384   13 [[nodiscard]] auto resolve(std::string_view host, std::string_view service)
385   { 385   {
HITCBC 386   13 return resolve_awaitable(*this, host, service, resolve_flags::none); 386   13 return resolve_awaitable(*this, host, service, resolve_flags::none);
387   } 387   }
388   388  
389   /** Initiate an asynchronous host-only resolve operation. 389   /** Initiate an asynchronous host-only resolve operation.
390   390  
391   Resolves a host name into its addresses, with no service or 391   Resolves a host name into its addresses, with no service or
392   port involved — the query `getaddrinfo` performs with a null 392   port involved — the query `getaddrinfo` performs with a null
393   service. Use this when the host and port travel separately, 393   service. Use this when the host and port travel separately,
394   as they do in most configuration. 394   as they do in most configuration.
395   395  
396   Each address appears once in the result even when the query 396   Each address appears once in the result even when the query
397   reports it more than once, and link-local results keep 397   reports it more than once, and link-local results keep
398   their zone. 398   their zone.
399   399  
400   @param host The host name or numeric address string. 400   @param host The host name or numeric address string.
401   401  
402   @return An awaitable that completes with 402   @return An awaitable that completes with
403   `io_result<std::vector<ip_address>>`. 403   `io_result<std::vector<ip_address>>`.
404   404  
405   @par Example 405   @par Example
406   @par !example host_only_resolve 406   @par !example host_only_resolve
407   */ 407   */
HITCBC 408   3 [[nodiscard]] auto resolve(std::string_view host) 408   3 [[nodiscard]] auto resolve(std::string_view host)
409   { 409   {
HITCBC 410   3 return resolve_host_awaitable(*this, host, resolve_flags::none); 410   3 return resolve_host_awaitable(*this, host, resolve_flags::none);
411   } 411   }
412   412  
413   /** Initiate an asynchronous host-only resolve operation with flags. 413   /** Initiate an asynchronous host-only resolve operation with flags.
414   414  
415   @param host The host name or numeric address string. 415   @param host The host name or numeric address string.
416   @param flags Resolution behavior flags. 416   @param flags Resolution behavior flags.
417   417  
418   @return An awaitable that completes with 418   @return An awaitable that completes with
419   `io_result<std::vector<ip_address>>`. 419   `io_result<std::vector<ip_address>>`.
420   */ 420   */
HITCBC 421   3 [[nodiscard]] auto resolve(std::string_view host, resolve_flags flags) 421   3 [[nodiscard]] auto resolve(std::string_view host, resolve_flags flags)
422   { 422   {
HITCBC 423   3 return resolve_host_awaitable(*this, host, flags); 423   3 return resolve_host_awaitable(*this, host, flags);
424   } 424   }
425   425  
426   /** Initiate an asynchronous resolve operation with flags. 426   /** Initiate an asynchronous resolve operation with flags.
427   427  
428   Resolves the host and service names into a list of endpoints. 428   Resolves the host and service names into a list of endpoints.
429   429  
430   This resolver must outlive the returned awaitable. 430   This resolver must outlive the returned awaitable.
431   431  
432   @param host A string identifying a location. 432   @param host A string identifying a location.
433   433  
434   @param service A string identifying the requested service. 434   @param service A string identifying the requested service.
435   435  
436   @param flags Flags controlling resolution behavior. 436   @param flags Flags controlling resolution behavior.
437   437  
438   @return An awaitable that completes with 438   @return An awaitable that completes with
439   `io_result<std::vector<endpoint>>`. 439   `io_result<std::vector<endpoint>>`.
440   */ 440   */
HITCBC 441   16 [[nodiscard]] auto resolve( 441   16 [[nodiscard]] auto resolve(
442   std::string_view host, std::string_view service, resolve_flags flags) 442   std::string_view host, std::string_view service, resolve_flags flags)
443   { 443   {
HITCBC 444   16 return resolve_awaitable(*this, host, service, flags); 444   16 return resolve_awaitable(*this, host, service, flags);
445   } 445   }
446   446  
447   /** Initiate an asynchronous reverse resolve operation. 447   /** Initiate an asynchronous reverse resolve operation.
448   448  
449   Resolves an endpoint into a hostname and service name using 449   Resolves an endpoint into a hostname and service name using
450   reverse DNS lookup (PTR record query). 450   reverse DNS lookup (PTR record query).
451   451  
452   This resolver must outlive the returned awaitable. 452   This resolver must outlive the returned awaitable.
453   453  
454   @param ep The endpoint to resolve. 454   @param ep The endpoint to resolve.
455   455  
456   @return An awaitable that completes with 456   @return An awaitable that completes with
457   `io_result<endpoint_name>`. 457   `io_result<endpoint_name>`.
458   458  
459   @par Example 459   @par Example
460   @par !example reverse_resolve 460   @par !example reverse_resolve
461   */ 461   */
HITCBC 462   11 [[nodiscard]] auto resolve(endpoint const& ep) 462   11 [[nodiscard]] auto resolve(endpoint const& ep)
463   { 463   {
HITCBC 464   11 return reverse_resolve_awaitable(*this, ep, reverse_flags::none); 464   11 return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
465   } 465   }
466   466  
467   /** Initiate an asynchronous reverse resolve operation with flags. 467   /** Initiate an asynchronous reverse resolve operation with flags.
468   468  
469   Resolves an endpoint into a hostname and service name using 469   Resolves an endpoint into a hostname and service name using
470   reverse DNS lookup (PTR record query). 470   reverse DNS lookup (PTR record query).
471   471  
472   This resolver must outlive the returned awaitable. 472   This resolver must outlive the returned awaitable.
473   473  
474   @param ep The endpoint to resolve. 474   @param ep The endpoint to resolve.
475   475  
476   @param flags Flags controlling resolution behavior. See reverse_flags. 476   @param flags Flags controlling resolution behavior. See reverse_flags.
477   477  
478   @return An awaitable that completes with 478   @return An awaitable that completes with
479   `io_result<endpoint_name>`. 479   `io_result<endpoint_name>`.
480   */ 480   */
HITCBC 481   9 [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags) 481   9 [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags)
482   { 482   {
HITCBC 483   9 return reverse_resolve_awaitable(*this, ep, flags); 483   9 return reverse_resolve_awaitable(*this, ep, flags);
484   } 484   }
485   485  
486   /** Cancel any pending asynchronous operations. 486   /** Cancel any pending asynchronous operations.
487   487  
488   Operations still in flight complete with `errc::operation_canceled`; 488   Operations still in flight complete with `errc::operation_canceled`;
489   an operation whose result is already decided reports that result. 489   an operation whose result is already decided reports that result.
490   Check `ec == cond::canceled` for portable comparison. 490   Check `ec == cond::canceled` for portable comparison.
491   */ 491   */
492   void cancel() noexcept; 492   void cancel() noexcept;
493   493  
494   public: 494   public:
495   /** Backend interface for DNS resolution operations. 495   /** Backend interface for DNS resolution operations.
496   496  
497   Platform backends derive from this to implement forward and 497   Platform backends derive from this to implement forward and
498   reverse DNS resolution via getaddrinfo/getnameinfo. 498   reverse DNS resolution via getaddrinfo/getnameinfo.
499   */ 499   */
500   struct implementation : io_object::implementation 500   struct implementation : io_object::implementation
501   { 501   {
502   /// Initiate an asynchronous forward DNS resolution. 502   /// Initiate an asynchronous forward DNS resolution.
503   virtual std::coroutine_handle<> resolve( 503   virtual std::coroutine_handle<> resolve(
504   std::coroutine_handle<>, 504   std::coroutine_handle<>,
505   capy::executor_ref, 505   capy::executor_ref,
506   std::string_view host, 506   std::string_view host,
507   std::string_view service, 507   std::string_view service,
508   resolve_flags flags, 508   resolve_flags flags,
509   std::stop_token, 509   std::stop_token,
510   std::error_code*, 510   std::error_code*,
511   std::vector<endpoint>*) = 0; 511   std::vector<endpoint>*) = 0;
512   512  
513   /// Initiate an asynchronous reverse DNS resolution. 513   /// Initiate an asynchronous reverse DNS resolution.
514   virtual std::coroutine_handle<> reverse_resolve( 514   virtual std::coroutine_handle<> reverse_resolve(
515   std::coroutine_handle<>, 515   std::coroutine_handle<>,
516   capy::executor_ref, 516   capy::executor_ref,
517   endpoint const& ep, 517   endpoint const& ep,
518   reverse_flags flags, 518   reverse_flags flags,
519   std::stop_token, 519   std::stop_token,
520   std::error_code*, 520   std::error_code*,
521   endpoint_name*) = 0; 521   endpoint_name*) = 0;
522   522  
523   /// Cancel pending resolve operations. 523   /// Cancel pending resolve operations.
524   virtual void cancel() noexcept = 0; 524   virtual void cancel() noexcept = 0;
525   }; 525   };
526   526  
527   protected: 527   protected:
528   explicit resolver(handle h) noexcept : io_object(std::move(h)) {} 528   explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
529   529  
530   private: 530   private:
HITCBC 531   59 inline implementation& get() const noexcept 531   59 inline implementation& get() const noexcept
532   { 532   {
HITCBC 533   59 return *static_cast<implementation*>(h_.get()); 533   59 return *static_cast<implementation*>(h_.get());
534   } 534   }
535   }; 535   };
536   536  
537   } // namespace boost::corosio 537   } // namespace boost::corosio
538   538  
539   #endif 539   #endif