1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_RESOLVER_HPP
10  
#ifndef BOOST_COROSIO_RESOLVER_HPP
11  
#define BOOST_COROSIO_RESOLVER_HPP
11  
#define BOOST_COROSIO_RESOLVER_HPP
12  

12  

13 -
#include <boost/corosio/detail/except.hpp>
 
14  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/endpoint.hpp>
14  
#include <boost/corosio/endpoint.hpp>
16  
#include <boost/corosio/io_object.hpp>
15  
#include <boost/corosio/io_object.hpp>
17  
#include <boost/capy/io_result.hpp>
16  
#include <boost/capy/io_result.hpp>
18  
#include <boost/corosio/resolver_results.hpp>
17  
#include <boost/corosio/resolver_results.hpp>
19  
#include <boost/capy/ex/executor_ref.hpp>
18  
#include <boost/capy/ex/executor_ref.hpp>
20  
#include <boost/capy/ex/execution_context.hpp>
19  
#include <boost/capy/ex/execution_context.hpp>
21  
#include <boost/capy/ex/io_env.hpp>
20  
#include <boost/capy/ex/io_env.hpp>
22  
#include <boost/capy/concept/executor.hpp>
21  
#include <boost/capy/concept/executor.hpp>
23  

22  

24  
#include <system_error>
23  
#include <system_error>
25  

24  

26  
#include <cassert>
25  
#include <cassert>
27  
#include <concepts>
26  
#include <concepts>
28  
#include <coroutine>
27  
#include <coroutine>
29  
#include <cstdint>
28  
#include <cstdint>
30  
#include <stop_token>
29  
#include <stop_token>
31  
#include <string>
30  
#include <string>
32  
#include <string_view>
31  
#include <string_view>
33  
#include <type_traits>
32  
#include <type_traits>
34  

33  

35  
namespace boost::corosio {
34  
namespace boost::corosio {
36  

35  

37  
/** Bitmask flags for resolver queries.
36  
/** Bitmask flags for resolver queries.
38  

37  

39  
    These flags correspond to the hints parameter of getaddrinfo.
38  
    These flags correspond to the hints parameter of getaddrinfo.
40  
*/
39  
*/
41  
enum class resolve_flags : unsigned int
40  
enum class resolve_flags : unsigned int
42  
{
41  
{
43  
    /// No flags.
42  
    /// No flags.
44  
    none = 0,
43  
    none = 0,
45  

44  

46  
    /// Indicate that returned endpoint is intended for use as a locally
45  
    /// Indicate that returned endpoint is intended for use as a locally
47  
    /// bound socket endpoint.
46  
    /// bound socket endpoint.
48  
    passive = 0x01,
47  
    passive = 0x01,
49  

48  

50  
    /// Host name should be treated as a numeric string defining an IPv4
49  
    /// Host name should be treated as a numeric string defining an IPv4
51  
    /// or IPv6 address and no name resolution should be attempted.
50  
    /// or IPv6 address and no name resolution should be attempted.
52  
    numeric_host = 0x04,
51  
    numeric_host = 0x04,
53  

52  

54  
    /// Service name should be treated as a numeric string defining a port
53  
    /// Service name should be treated as a numeric string defining a port
55  
    /// number and no name resolution should be attempted.
54  
    /// number and no name resolution should be attempted.
56  
    numeric_service = 0x08,
55  
    numeric_service = 0x08,
57  

56  

58  
    /// Only return IPv4 addresses if a non-loopback IPv4 address is
57  
    /// Only return IPv4 addresses if a non-loopback IPv4 address is
59  
    /// configured for the system. Only return IPv6 addresses if a
58  
    /// configured for the system. Only return IPv6 addresses if a
60  
    /// non-loopback IPv6 address is configured for the system.
59  
    /// non-loopback IPv6 address is configured for the system.
61  
    address_configured = 0x20,
60  
    address_configured = 0x20,
62  

61  

63  
    /// If the query protocol family is specified as IPv6, return
62  
    /// If the query protocol family is specified as IPv6, return
64  
    /// IPv4-mapped IPv6 addresses on finding no IPv6 addresses.
63  
    /// IPv4-mapped IPv6 addresses on finding no IPv6 addresses.
65  
    v4_mapped = 0x800,
64  
    v4_mapped = 0x800,
66  

65  

67  
    /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses.
66  
    /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses.
68  
    all_matching = 0x100
67  
    all_matching = 0x100
69  
};
68  
};
70  

69  

71  
/** Combine two resolve_flags. */
70  
/** Combine two resolve_flags. */
72 -
inline
71 +
inline resolve_flags
73 -
resolve_flags
 
74  
operator|(resolve_flags a, resolve_flags b) noexcept
72  
operator|(resolve_flags a, resolve_flags b) noexcept
75  
{
73  
{
76  
    return static_cast<resolve_flags>(
74  
    return static_cast<resolve_flags>(
77 -
        static_cast<unsigned int>(a) |
75 +
        static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
78 -
        static_cast<unsigned int>(b));
 
79  
}
76  
}
80  

77  

81  
/** Combine two resolve_flags. */
78  
/** Combine two resolve_flags. */
82 -
inline
79 +
inline resolve_flags&
83 -
resolve_flags&
 
84  
operator|=(resolve_flags& a, resolve_flags b) noexcept
80  
operator|=(resolve_flags& a, resolve_flags b) noexcept
85  
{
81  
{
86  
    a = a | b;
82  
    a = a | b;
87  
    return a;
83  
    return a;
88  
}
84  
}
89  

85  

90  
/** Intersect two resolve_flags. */
86  
/** Intersect two resolve_flags. */
91 -
inline
87 +
inline resolve_flags
92 -
resolve_flags
 
93  
operator&(resolve_flags a, resolve_flags b) noexcept
88  
operator&(resolve_flags a, resolve_flags b) noexcept
94  
{
89  
{
95  
    return static_cast<resolve_flags>(
90  
    return static_cast<resolve_flags>(
96 -
        static_cast<unsigned int>(a) &
91 +
        static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
97 -
        static_cast<unsigned int>(b));
 
98  
}
92  
}
99  

93  

100  
/** Intersect two resolve_flags. */
94  
/** Intersect two resolve_flags. */
101 -
inline
95 +
inline resolve_flags&
102 -
resolve_flags&
 
103  
operator&=(resolve_flags& a, resolve_flags b) noexcept
96  
operator&=(resolve_flags& a, resolve_flags b) noexcept
104  
{
97  
{
105  
    a = a & b;
98  
    a = a & b;
106  
    return a;
99  
    return a;
107  
}
100  
}
108 -
//------------------------------------------------------------------------------
 
109  

101  

110  

102  

111  
/** Bitmask flags for reverse resolver queries.
103  
/** Bitmask flags for reverse resolver queries.
112  

104  

113  
    These flags correspond to the flags parameter of getnameinfo.
105  
    These flags correspond to the flags parameter of getnameinfo.
114  
*/
106  
*/
115  
enum class reverse_flags : unsigned int
107  
enum class reverse_flags : unsigned int
116  
{
108  
{
117  
    /// No flags.
109  
    /// No flags.
118  
    none = 0,
110  
    none = 0,
119  

111  

120  
    /// Return the numeric form of the hostname instead of its name.
112  
    /// Return the numeric form of the hostname instead of its name.
121  
    numeric_host = 0x01,
113  
    numeric_host = 0x01,
122  

114  

123  
    /// Return the numeric form of the service name instead of its name.
115  
    /// Return the numeric form of the service name instead of its name.
124  
    numeric_service = 0x02,
116  
    numeric_service = 0x02,
125  

117  

126  
    /// Return an error if the hostname cannot be resolved.
118  
    /// Return an error if the hostname cannot be resolved.
127  
    name_required = 0x04,
119  
    name_required = 0x04,
128  

120  

129  
    /// Lookup for datagram (UDP) service instead of stream (TCP).
121  
    /// Lookup for datagram (UDP) service instead of stream (TCP).
130  
    datagram_service = 0x08
122  
    datagram_service = 0x08
131  
};
123  
};
132  

124  

133  
/** Combine two reverse_flags. */
125  
/** Combine two reverse_flags. */
134 -
inline
126 +
inline reverse_flags
135 -
reverse_flags
 
136  
operator|(reverse_flags a, reverse_flags b) noexcept
127  
operator|(reverse_flags a, reverse_flags b) noexcept
137  
{
128  
{
138  
    return static_cast<reverse_flags>(
129  
    return static_cast<reverse_flags>(
139 -
        static_cast<unsigned int>(a) |
130 +
        static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
140 -
        static_cast<unsigned int>(b));
 
141  
}
131  
}
142  

132  

143  
/** Combine two reverse_flags. */
133  
/** Combine two reverse_flags. */
144 -
inline
134 +
inline reverse_flags&
145 -
reverse_flags&
 
146  
operator|=(reverse_flags& a, reverse_flags b) noexcept
135  
operator|=(reverse_flags& a, reverse_flags b) noexcept
147  
{
136  
{
148  
    a = a | b;
137  
    a = a | b;
149  
    return a;
138  
    return a;
150  
}
139  
}
151  

140  

152  
/** Intersect two reverse_flags. */
141  
/** Intersect two reverse_flags. */
153 -
inline
142 +
inline reverse_flags
154 -
reverse_flags
 
155  
operator&(reverse_flags a, reverse_flags b) noexcept
143  
operator&(reverse_flags a, reverse_flags b) noexcept
156  
{
144  
{
157  
    return static_cast<reverse_flags>(
145  
    return static_cast<reverse_flags>(
158 -
        static_cast<unsigned int>(a) &
146 +
        static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
159 -
        static_cast<unsigned int>(b));
 
160  
}
147  
}
161  

148  

162  
/** Intersect two reverse_flags. */
149  
/** Intersect two reverse_flags. */
163 -
inline
150 +
inline reverse_flags&
164 -
reverse_flags&
 
165  
operator&=(reverse_flags& a, reverse_flags b) noexcept
151  
operator&=(reverse_flags& a, reverse_flags b) noexcept
166  
{
152  
{
167  
    a = a & b;
153  
    a = a & b;
168  
    return a;
154  
    return a;
169  
}
155  
}
170 -
//------------------------------------------------------------------------------
 
171  

156  

172  

157  

173  
/** An asynchronous DNS resolver for coroutine I/O.
158  
/** An asynchronous DNS resolver for coroutine I/O.
174  

159  

175  
    This class provides asynchronous DNS resolution operations that return
160  
    This class provides asynchronous DNS resolution operations that return
176  
    awaitable types. Each operation participates in the affine awaitable
161  
    awaitable types. Each operation participates in the affine awaitable
177  
    protocol, ensuring coroutines resume on the correct executor.
162  
    protocol, ensuring coroutines resume on the correct executor.
178  

163  

179  
    @par Thread Safety
164  
    @par Thread Safety
180  
    Distinct objects: Safe.@n
165  
    Distinct objects: Safe.@n
181  
    Shared objects: Unsafe. A resolver must not have concurrent resolve
166  
    Shared objects: Unsafe. A resolver must not have concurrent resolve
182  
    operations.
167  
    operations.
183  

168  

184  
    @par Semantics
169  
    @par Semantics
185  
    Wraps platform DNS resolution (getaddrinfo/getnameinfo).
170  
    Wraps platform DNS resolution (getaddrinfo/getnameinfo).
186  
    Operations dispatch to OS resolver APIs via the io_context
171  
    Operations dispatch to OS resolver APIs via the io_context
187  
    thread pool.
172  
    thread pool.
188  

173  

189  
    @par Example
174  
    @par Example
190  
    @code
175  
    @code
191  
    io_context ioc;
176  
    io_context ioc;
192  
    resolver r(ioc);
177  
    resolver r(ioc);
193  

178  

194  
    // Using structured bindings
179  
    // Using structured bindings
195  
    auto [ec, results] = co_await r.resolve("www.example.com", "https");
180  
    auto [ec, results] = co_await r.resolve("www.example.com", "https");
196  
    if (ec)
181  
    if (ec)
197  
        co_return;
182  
        co_return;
198  

183  

199  
    for (auto const& entry : results)
184  
    for (auto const& entry : results)
200  
        std::cout << entry.get_endpoint().port() << std::endl;
185  
        std::cout << entry.get_endpoint().port() << std::endl;
201  

186  

202  
    // Or using exceptions
187  
    // Or using exceptions
203  
    auto results = (co_await r.resolve("www.example.com", "https")).value();
188  
    auto results = (co_await r.resolve("www.example.com", "https")).value();
204  
    @endcode
189  
    @endcode
205  
*/
190  
*/
206  
class BOOST_COROSIO_DECL resolver : public io_object
191  
class BOOST_COROSIO_DECL resolver : public io_object
207  
{
192  
{
208  
    struct resolve_awaitable
193  
    struct resolve_awaitable
209  
    {
194  
    {
210  
        resolver& r_;
195  
        resolver& r_;
211  
        std::string host_;
196  
        std::string host_;
212  
        std::string service_;
197  
        std::string service_;
213  
        resolve_flags flags_;
198  
        resolve_flags flags_;
214  
        std::stop_token token_;
199  
        std::stop_token token_;
215  
        mutable std::error_code ec_;
200  
        mutable std::error_code ec_;
216  
        mutable resolver_results results_;
201  
        mutable resolver_results results_;
217  

202  

218  
        resolve_awaitable(
203  
        resolve_awaitable(
219  
            resolver& r,
204  
            resolver& r,
220  
            std::string_view host,
205  
            std::string_view host,
221  
            std::string_view service,
206  
            std::string_view service,
222  
            resolve_flags flags) noexcept
207  
            resolve_flags flags) noexcept
223  
            : r_(r)
208  
            : r_(r)
224  
            , host_(host)
209  
            , host_(host)
225  
            , service_(service)
210  
            , service_(service)
226  
            , flags_(flags)
211  
            , flags_(flags)
227  
        {
212  
        {
228  
        }
213  
        }
229  

214  

230  
        bool await_ready() const noexcept
215  
        bool await_ready() const noexcept
231  
        {
216  
        {
232  
            return token_.stop_requested();
217  
            return token_.stop_requested();
233  
        }
218  
        }
234  

219  

235  
        capy::io_result<resolver_results> await_resume() const noexcept
220  
        capy::io_result<resolver_results> await_resume() const noexcept
236  
        {
221  
        {
237  
            if (token_.stop_requested())
222  
            if (token_.stop_requested())
238  
                return {make_error_code(std::errc::operation_canceled), {}};
223  
                return {make_error_code(std::errc::operation_canceled), {}};
239  
            return {ec_, std::move(results_)};
224  
            return {ec_, std::move(results_)};
240  
        }
225  
        }
241  

226  

242 -
        auto await_suspend(
227 +
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
243 -
            std::coroutine_handle<> h,
228 +
            -> std::coroutine_handle<>
244 -
            capy::io_env const* env) -> std::coroutine_handle<>
 
245  
        {
229  
        {
246  
            token_ = env->stop_token;
230  
            token_ = env->stop_token;
247 -
            return r_.get().resolve(h, env->executor, host_, service_, flags_, token_, &ec_, &results_);
231 +
            return r_.get().resolve(
 
232 +
                h, env->executor, host_, service_, flags_, token_, &ec_,
 
233 +
                &results_);
248  
        }
234  
        }
249  
    };
235  
    };
250  

236  

251  
    struct reverse_resolve_awaitable
237  
    struct reverse_resolve_awaitable
252  
    {
238  
    {
253  
        resolver& r_;
239  
        resolver& r_;
254  
        endpoint ep_;
240  
        endpoint ep_;
255  
        reverse_flags flags_;
241  
        reverse_flags flags_;
256  
        std::stop_token token_;
242  
        std::stop_token token_;
257  
        mutable std::error_code ec_;
243  
        mutable std::error_code ec_;
258  
        mutable reverse_resolver_result result_;
244  
        mutable reverse_resolver_result result_;
259  

245  

260  
        reverse_resolve_awaitable(
246  
        reverse_resolve_awaitable(
261 -
            resolver& r,
247 +
            resolver& r, endpoint const& ep, reverse_flags flags) noexcept
262 -
            endpoint const& ep,
 
263 -
            reverse_flags flags) noexcept
 
264  
            : r_(r)
248  
            : r_(r)
265  
            , ep_(ep)
249  
            , ep_(ep)
266  
            , flags_(flags)
250  
            , flags_(flags)
267  
        {
251  
        {
268  
        }
252  
        }
269  

253  

270  
        bool await_ready() const noexcept
254  
        bool await_ready() const noexcept
271  
        {
255  
        {
272  
            return token_.stop_requested();
256  
            return token_.stop_requested();
273  
        }
257  
        }
274  

258  

275  
        capy::io_result<reverse_resolver_result> await_resume() const noexcept
259  
        capy::io_result<reverse_resolver_result> await_resume() const noexcept
276  
        {
260  
        {
277  
            if (token_.stop_requested())
261  
            if (token_.stop_requested())
278  
                return {make_error_code(std::errc::operation_canceled), {}};
262  
                return {make_error_code(std::errc::operation_canceled), {}};
279  
            return {ec_, std::move(result_)};
263  
            return {ec_, std::move(result_)};
280  
        }
264  
        }
281  

265  

282 -
        auto await_suspend(
266 +
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
283 -
            std::coroutine_handle<> h,
267 +
            -> std::coroutine_handle<>
284 -
            capy::io_env const* env) -> std::coroutine_handle<>
 
285  
        {
268  
        {
286  
            token_ = env->stop_token;
269  
            token_ = env->stop_token;
287 -
            return r_.get().reverse_resolve(h, env->executor, ep_, flags_, token_, &ec_, &result_);
270 +
            return r_.get().reverse_resolve(
 
271 +
                h, env->executor, ep_, flags_, token_, &ec_, &result_);
288  
        }
272  
        }
289  
    };
273  
    };
290  

274  

291  
public:
275  
public:
292  
    /** Destructor.
276  
    /** Destructor.
293  

277  

294  
        Cancels any pending operations.
278  
        Cancels any pending operations.
295  
    */
279  
    */
296 -
    ~resolver();
280 +
    ~resolver() override;
297  

281  

298  
    /** Construct a resolver from an execution context.
282  
    /** Construct a resolver from an execution context.
299  

283  

300  
        @param ctx The execution context that will own this resolver.
284  
        @param ctx The execution context that will own this resolver.
301  
    */
285  
    */
302  
    explicit resolver(capy::execution_context& ctx);
286  
    explicit resolver(capy::execution_context& ctx);
303  

287  

304  
    /** Construct a resolver from an executor.
288  
    /** Construct a resolver from an executor.
305  

289  

306  
        The resolver is associated with the executor's context.
290  
        The resolver is associated with the executor's context.
307  

291  

308  
        @param ex The executor whose context will own the resolver.
292  
        @param ex The executor whose context will own the resolver.
309  
    */
293  
    */
310  
    template<class Ex>
294  
    template<class Ex>
311 -
        requires (!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
295 +
        requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
312 -
                 capy::Executor<Ex>
296 +
        capy::Executor<Ex>
313 -
    explicit resolver(Ex const& ex)
297 +
    explicit resolver(Ex const& ex) : resolver(ex.context())
314 -
        : resolver(ex.context())
 
315  
    {
298  
    {
316  
    }
299  
    }
317  

300  

318  
    /** Move constructor.
301  
    /** Move constructor.
319  

302  

320  
        Transfers ownership of the resolver resources.
303  
        Transfers ownership of the resolver resources.
321  

304  

322  
        @param other The resolver to move from.
305  
        @param other The resolver to move from.
323  
    */
306  
    */
324 -
    resolver(resolver&& other) noexcept
307 +
    resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
325 -
        : io_object(std::move(other))
 
326 -
    {
 
327 -
    }
 
328  

308  

329  
    /** Move assignment operator.
309  
    /** Move assignment operator.
330  

310  

331  
        Cancels any existing operations and transfers ownership.
311  
        Cancels any existing operations and transfers ownership.
332  
        The source and destination must share the same execution context.
312  
        The source and destination must share the same execution context.
333  

313  

334  
        @param other The resolver to move from.
314  
        @param other The resolver to move from.
335  

315  

336 -

 
337 -
        @throws std::logic_error if the resolvers have different
 
338 -
            execution contexts.
 
339  
        @return Reference to this resolver.
316  
        @return Reference to this resolver.
340  
    */
317  
    */
341 -
    resolver& operator=(resolver&& other)
318 +
    resolver& operator=(resolver&& other) noexcept
342  
    {
319  
    {
343 -
        {
 
344 -
            if (&context() != &other.context())
 
345 -
                detail::throw_logic_error(
 
346 -
                    "cannot move resolver across execution contexts");
 
347  
        if (this != &other)
320  
        if (this != &other)
348 -
        }
 
349  
            h_ = std::move(other.h_);
321  
            h_ = std::move(other.h_);
350  
        return *this;
322  
        return *this;
351  
    }
323  
    }
352  

324  

353  
    resolver(resolver const&) = delete;
325  
    resolver(resolver const&) = delete;
354  
    resolver& operator=(resolver const&) = delete;
326  
    resolver& operator=(resolver const&) = delete;
355  

327  

356  
    /** Initiate an asynchronous resolve operation.
328  
    /** Initiate an asynchronous resolve operation.
357  

329  

358  
        Resolves the host and service names into a list of endpoints.
330  
        Resolves the host and service names into a list of endpoints.
359  

331  

360  
        @param host A string identifying a location. May be a descriptive
332  
        @param host A string identifying a location. May be a descriptive
361  
            name or a numeric address string.
333  
            name or a numeric address string.
362  

334  

363  
        @param service A string identifying the requested service. This may
335  
        @param service A string identifying the requested service. This may
364  
            be a descriptive name or a numeric string corresponding to a
336  
            be a descriptive name or a numeric string corresponding to a
365  
            port number.
337  
            port number.
366  

338  

367  
        @return An awaitable that completes with `io_result<resolver_results>`.
339  
        @return An awaitable that completes with `io_result<resolver_results>`.
368  

340  

369  
        @par Example
341  
        @par Example
370  
        @code
342  
        @code
371  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
343  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
372  
        @endcode
344  
        @endcode
373  
    */
345  
    */
374 -
    auto resolve(
346 +
    auto resolve(std::string_view host, std::string_view service)
375 -
        std::string_view host,
 
376 -
        std::string_view service)
 
377  
    {
347  
    {
378  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
348  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
379  
    }
349  
    }
380  

350  

381  
    /** Initiate an asynchronous resolve operation with flags.
351  
    /** Initiate an asynchronous resolve operation with flags.
382  

352  

383  
        Resolves the host and service names into a list of endpoints.
353  
        Resolves the host and service names into a list of endpoints.
384  

354  

385  
        @param host A string identifying a location.
355  
        @param host A string identifying a location.
386  

356  

387  
        @param service A string identifying the requested service.
357  
        @param service A string identifying the requested service.
388  

358  

389  
        @param flags Flags controlling resolution behavior.
359  
        @param flags Flags controlling resolution behavior.
390  

360  

391  
        @return An awaitable that completes with `io_result<resolver_results>`.
361  
        @return An awaitable that completes with `io_result<resolver_results>`.
392  
    */
362  
    */
393  
    auto resolve(
363  
    auto resolve(
394 -
        std::string_view host,
364 +
        std::string_view host, std::string_view service, resolve_flags flags)
395 -
        std::string_view service,
 
396 -
        resolve_flags flags)
 
397  
    {
365  
    {
398  
        return resolve_awaitable(*this, host, service, flags);
366  
        return resolve_awaitable(*this, host, service, flags);
399  
    }
367  
    }
400  

368  

401  
    /** Initiate an asynchronous reverse resolve operation.
369  
    /** Initiate an asynchronous reverse resolve operation.
402  

370  

403  
        Resolves an endpoint into a hostname and service name using
371  
        Resolves an endpoint into a hostname and service name using
404  
        reverse DNS lookup (PTR record query).
372  
        reverse DNS lookup (PTR record query).
405  

373  

406  
        @param ep The endpoint to resolve.
374  
        @param ep The endpoint to resolve.
407  

375  

408  
        @return An awaitable that completes with
376  
        @return An awaitable that completes with
409  
            `io_result<reverse_resolver_result>`.
377  
            `io_result<reverse_resolver_result>`.
410  

378  

411  
        @par Example
379  
        @par Example
412  
        @code
380  
        @code
413  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
381  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
414  
        auto [ec, result] = co_await r.resolve(ep);
382  
        auto [ec, result] = co_await r.resolve(ep);
415  
        if (!ec)
383  
        if (!ec)
416  
            std::cout << result.host_name() << ":" << result.service_name();
384  
            std::cout << result.host_name() << ":" << result.service_name();
417  
        @endcode
385  
        @endcode
418  
    */
386  
    */
419  
    auto resolve(endpoint const& ep)
387  
    auto resolve(endpoint const& ep)
420  
    {
388  
    {
421  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
389  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
422  
    }
390  
    }
423  

391  

424  
    /** Initiate an asynchronous reverse resolve operation with flags.
392  
    /** Initiate an asynchronous reverse resolve operation with flags.
425  

393  

426  
        Resolves an endpoint into a hostname and service name using
394  
        Resolves an endpoint into a hostname and service name using
427  
        reverse DNS lookup (PTR record query).
395  
        reverse DNS lookup (PTR record query).
428  

396  

429  
        @param ep The endpoint to resolve.
397  
        @param ep The endpoint to resolve.
430  

398  

431  
        @param flags Flags controlling resolution behavior. See reverse_flags.
399  
        @param flags Flags controlling resolution behavior. See reverse_flags.
432  

400  

433  
        @return An awaitable that completes with
401  
        @return An awaitable that completes with
434  
            `io_result<reverse_resolver_result>`.
402  
            `io_result<reverse_resolver_result>`.
435  
    */
403  
    */
436  
    auto resolve(endpoint const& ep, reverse_flags flags)
404  
    auto resolve(endpoint const& ep, reverse_flags flags)
437  
    {
405  
    {
438  
        return reverse_resolve_awaitable(*this, ep, flags);
406  
        return reverse_resolve_awaitable(*this, ep, flags);
439  
    }
407  
    }
440  

408  

441  
    /** Cancel any pending asynchronous operations.
409  
    /** Cancel any pending asynchronous operations.
442  

410  

443  
        All outstanding operations complete with `errc::operation_canceled`.
411  
        All outstanding operations complete with `errc::operation_canceled`.
444  
        Check `ec == cond::canceled` for portable comparison.
412  
        Check `ec == cond::canceled` for portable comparison.
445  
    */
413  
    */
446  
    void cancel();
414  
    void cancel();
447  

415  

448  
public:
416  
public:
449  
    struct implementation : io_object::implementation
417  
    struct implementation : io_object::implementation
450  
    {
418  
    {
451  
        virtual std::coroutine_handle<> resolve(
419  
        virtual std::coroutine_handle<> resolve(
452  
            std::coroutine_handle<>,
420  
            std::coroutine_handle<>,
453  
            capy::executor_ref,
421  
            capy::executor_ref,
454  
            std::string_view host,
422  
            std::string_view host,
455  
            std::string_view service,
423  
            std::string_view service,
456  
            resolve_flags flags,
424  
            resolve_flags flags,
457  
            std::stop_token,
425  
            std::stop_token,
458  
            std::error_code*,
426  
            std::error_code*,
459  
            resolver_results*) = 0;
427  
            resolver_results*) = 0;
460  

428  

461  
        virtual std::coroutine_handle<> reverse_resolve(
429  
        virtual std::coroutine_handle<> reverse_resolve(
462  
            std::coroutine_handle<>,
430  
            std::coroutine_handle<>,
463  
            capy::executor_ref,
431  
            capy::executor_ref,
464  
            endpoint const& ep,
432  
            endpoint const& ep,
465  
            reverse_flags flags,
433  
            reverse_flags flags,
466  
            std::stop_token,
434  
            std::stop_token,
467  
            std::error_code*,
435  
            std::error_code*,
468  
            reverse_resolver_result*) = 0;
436  
            reverse_resolver_result*) = 0;
469  

437  

470  
        virtual void cancel() noexcept = 0;
438  
        virtual void cancel() noexcept = 0;
471  
    };
439  
    };
472  

440  

473  
private:
441  
private:
474  
    inline implementation& get() const noexcept
442  
    inline implementation& get() const noexcept
475  
    {
443  
    {
476  
        return *static_cast<implementation*>(h_.get());
444  
        return *static_cast<implementation*>(h_.get());
477  
    }
445  
    }
478  
};
446  
};
479  

447  

480  
} // namespace boost::corosio
448  
} // namespace boost::corosio
481  

449  

482  
#endif
450  
#endif