1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/except.hpp>
14  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/endpoint.hpp>
15  
#include <boost/corosio/endpoint.hpp>
16  
#include <boost/corosio/io_object.hpp>
16  
#include <boost/corosio/io_object.hpp>
17  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/corosio/resolver_results.hpp>
18  
#include <boost/corosio/resolver_results.hpp>
19  
#include <boost/capy/ex/executor_ref.hpp>
19  
#include <boost/capy/ex/executor_ref.hpp>
20  
#include <boost/capy/ex/execution_context.hpp>
20  
#include <boost/capy/ex/execution_context.hpp>
21  
#include <boost/capy/concept/executor.hpp>
21  
#include <boost/capy/concept/executor.hpp>
22  

22  

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

24  

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

33  

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

35  

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

37  

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

44  

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

48  

49  
    /// 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
50  
    /// or IPv6 address and no name resolution should be attempted.
50  
    /// or IPv6 address and no name resolution should be attempted.
51  
    numeric_host = 0x04,
51  
    numeric_host = 0x04,
52  

52  

53  
    /// 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
54  
    /// number and no name resolution should be attempted.
54  
    /// number and no name resolution should be attempted.
55  
    numeric_service = 0x08,
55  
    numeric_service = 0x08,
56  

56  

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

61  

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

65  

66  
    /// 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.
67  
    all_matching = 0x100
67  
    all_matching = 0x100
68  
};
68  
};
69  

69  

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

79  

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

88  

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

98  

99  
/** Intersect two resolve_flags. */
99  
/** Intersect two resolve_flags. */
100  
inline
100  
inline
101  
resolve_flags&
101  
resolve_flags&
102  
operator&=(resolve_flags& a, resolve_flags b) noexcept
102  
operator&=(resolve_flags& a, resolve_flags b) noexcept
103  
{
103  
{
104  
    a = a & b;
104  
    a = a & b;
105  
    return a;
105  
    return a;
106  
}
106  
}
107  

107  

108  
//------------------------------------------------------------------------------
108  
//------------------------------------------------------------------------------
109  

109  

110  
/** Bitmask flags for reverse resolver queries.
110  
/** Bitmask flags for reverse resolver queries.
111  

111  

112  
    These flags correspond to the flags parameter of getnameinfo.
112  
    These flags correspond to the flags parameter of getnameinfo.
113  
*/
113  
*/
114  
enum class reverse_flags : unsigned int
114  
enum class reverse_flags : unsigned int
115  
{
115  
{
116  
    /// No flags.
116  
    /// No flags.
117  
    none = 0,
117  
    none = 0,
118  

118  

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

121  

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

124  

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

127  

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

131  

132  
/** Combine two reverse_flags. */
132  
/** Combine two reverse_flags. */
133  
inline
133  
inline
134  
reverse_flags
134  
reverse_flags
135  
operator|(reverse_flags a, reverse_flags b) noexcept
135  
operator|(reverse_flags a, reverse_flags b) noexcept
136  
{
136  
{
137  
    return static_cast<reverse_flags>(
137  
    return static_cast<reverse_flags>(
138  
        static_cast<unsigned int>(a) |
138  
        static_cast<unsigned int>(a) |
139  
        static_cast<unsigned int>(b));
139  
        static_cast<unsigned int>(b));
140  
}
140  
}
141  

141  

142  
/** Combine two reverse_flags. */
142  
/** Combine two reverse_flags. */
143  
inline
143  
inline
144  
reverse_flags&
144  
reverse_flags&
145  
operator|=(reverse_flags& a, reverse_flags b) noexcept
145  
operator|=(reverse_flags& a, reverse_flags b) noexcept
146  
{
146  
{
147  
    a = a | b;
147  
    a = a | b;
148  
    return a;
148  
    return a;
149  
}
149  
}
150  

150  

151  
/** Intersect two reverse_flags. */
151  
/** Intersect two reverse_flags. */
152  
inline
152  
inline
153  
reverse_flags
153  
reverse_flags
154  
operator&(reverse_flags a, reverse_flags b) noexcept
154  
operator&(reverse_flags a, reverse_flags b) noexcept
155  
{
155  
{
156  
    return static_cast<reverse_flags>(
156  
    return static_cast<reverse_flags>(
157  
        static_cast<unsigned int>(a) &
157  
        static_cast<unsigned int>(a) &
158  
        static_cast<unsigned int>(b));
158  
        static_cast<unsigned int>(b));
159  
}
159  
}
160  

160  

161  
/** Intersect two reverse_flags. */
161  
/** Intersect two reverse_flags. */
162  
inline
162  
inline
163  
reverse_flags&
163  
reverse_flags&
164  
operator&=(reverse_flags& a, reverse_flags b) noexcept
164  
operator&=(reverse_flags& a, reverse_flags b) noexcept
165  
{
165  
{
166  
    a = a & b;
166  
    a = a & b;
167  
    return a;
167  
    return a;
168  
}
168  
}
169  

169  

170  
//------------------------------------------------------------------------------
170  
//------------------------------------------------------------------------------
171  

171  

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

173  

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

177  

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

182  

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

187  

188  
    @par Example
188  
    @par Example
189  
    @code
189  
    @code
190  
    io_context ioc;
190  
    io_context ioc;
191  
    resolver r(ioc);
191  
    resolver r(ioc);
192  

192  

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

197  

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

200  

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

216  

217  
        resolve_awaitable(
217  
        resolve_awaitable(
218  
            resolver& r,
218  
            resolver& r,
219  
            std::string_view host,
219  
            std::string_view host,
220  
            std::string_view service,
220  
            std::string_view service,
221  
            resolve_flags flags) noexcept
221  
            resolve_flags flags) noexcept
222  
            : r_(r)
222  
            : r_(r)
223  
            , host_(host)
223  
            , host_(host)
224  
            , service_(service)
224  
            , service_(service)
225  
            , flags_(flags)
225  
            , flags_(flags)
226  
        {
226  
        {
227  
        }
227  
        }
228  

228  

229  
        bool await_ready() const noexcept
229  
        bool await_ready() const noexcept
230  
        {
230  
        {
231  
            return token_.stop_requested();
231  
            return token_.stop_requested();
232  
        }
232  
        }
233  

233  

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

240  

241  
        template<typename Ex>
241  
        template<typename Ex>
242  
        auto await_suspend(
242  
        auto await_suspend(
243  
            std::coroutine_handle<> h,
243  
            std::coroutine_handle<> h,
244  
            Ex const& ex) -> std::coroutine_handle<>
244  
            Ex const& ex) -> std::coroutine_handle<>
245  
        {
245  
        {
246  
            return r_.get().resolve(h, ex, host_, service_, flags_, token_, &ec_, &results_);
246  
            return r_.get().resolve(h, ex, host_, service_, flags_, token_, &ec_, &results_);
247  
        }
247  
        }
248  

248  

249  
        template<typename Ex>
249  
        template<typename Ex>
250  
        auto await_suspend(
250  
        auto await_suspend(
251  
            std::coroutine_handle<> h,
251  
            std::coroutine_handle<> h,
252  
            Ex const& ex,
252  
            Ex const& ex,
253  
            std::stop_token token) -> std::coroutine_handle<>
253  
            std::stop_token token) -> std::coroutine_handle<>
254  
        {
254  
        {
255  
            token_ = std::move(token);
255  
            token_ = std::move(token);
256  
            return r_.get().resolve(h, ex, host_, service_, flags_, token_, &ec_, &results_);
256  
            return r_.get().resolve(h, ex, host_, service_, flags_, token_, &ec_, &results_);
257  
        }
257  
        }
258  
    };
258  
    };
259  

259  

260  
    struct reverse_resolve_awaitable
260  
    struct reverse_resolve_awaitable
261  
    {
261  
    {
262  
        resolver& r_;
262  
        resolver& r_;
263  
        endpoint ep_;
263  
        endpoint ep_;
264  
        reverse_flags flags_;
264  
        reverse_flags flags_;
265  
        std::stop_token token_;
265  
        std::stop_token token_;
266  
        mutable std::error_code ec_;
266  
        mutable std::error_code ec_;
267  
        mutable reverse_resolver_result result_;
267  
        mutable reverse_resolver_result result_;
268  

268  

269  
        reverse_resolve_awaitable(
269  
        reverse_resolve_awaitable(
270  
            resolver& r,
270  
            resolver& r,
271  
            endpoint const& ep,
271  
            endpoint const& ep,
272  
            reverse_flags flags) noexcept
272  
            reverse_flags flags) noexcept
273  
            : r_(r)
273  
            : r_(r)
274  
            , ep_(ep)
274  
            , ep_(ep)
275  
            , flags_(flags)
275  
            , flags_(flags)
276  
        {
276  
        {
277  
        }
277  
        }
278  

278  

279  
        bool await_ready() const noexcept
279  
        bool await_ready() const noexcept
280  
        {
280  
        {
281  
            return token_.stop_requested();
281  
            return token_.stop_requested();
282  
        }
282  
        }
283  

283  

284  
        capy::io_result<reverse_resolver_result> await_resume() const noexcept
284  
        capy::io_result<reverse_resolver_result> await_resume() const noexcept
285  
        {
285  
        {
286  
            if (token_.stop_requested())
286  
            if (token_.stop_requested())
287  
                return {make_error_code(std::errc::operation_canceled), {}};
287  
                return {make_error_code(std::errc::operation_canceled), {}};
288  
            return {ec_, std::move(result_)};
288  
            return {ec_, std::move(result_)};
289  
        }
289  
        }
290  

290  

291  
        template<typename Ex>
291  
        template<typename Ex>
292  
        auto await_suspend(
292  
        auto await_suspend(
293  
            std::coroutine_handle<> h,
293  
            std::coroutine_handle<> h,
294  
            Ex const& ex) -> std::coroutine_handle<>
294  
            Ex const& ex) -> std::coroutine_handle<>
295  
        {
295  
        {
296  
            return r_.get().reverse_resolve(h, ex, ep_, flags_, token_, &ec_, &result_);
296  
            return r_.get().reverse_resolve(h, ex, ep_, flags_, token_, &ec_, &result_);
297  
        }
297  
        }
298  

298  

299  
        template<typename Ex>
299  
        template<typename Ex>
300  
        auto await_suspend(
300  
        auto await_suspend(
301  
            std::coroutine_handle<> h,
301  
            std::coroutine_handle<> h,
302  
            Ex const& ex,
302  
            Ex const& ex,
303  
            std::stop_token token) -> std::coroutine_handle<>
303  
            std::stop_token token) -> std::coroutine_handle<>
304  
        {
304  
        {
305  
            token_ = std::move(token);
305  
            token_ = std::move(token);
306  
            return r_.get().reverse_resolve(h, ex, ep_, flags_, token_, &ec_, &result_);
306  
            return r_.get().reverse_resolve(h, ex, ep_, flags_, token_, &ec_, &result_);
307  
        }
307  
        }
308  
    };
308  
    };
309  

309  

310  
public:
310  
public:
311  
    /** Destructor.
311  
    /** Destructor.
312  

312  

313  
        Cancels any pending operations.
313  
        Cancels any pending operations.
314  
    */
314  
    */
315  
    ~resolver();
315  
    ~resolver();
316  

316  

317  
    /** Construct a resolver from an execution context.
317  
    /** Construct a resolver from an execution context.
318  

318  

319  
        @param ctx The execution context that will own this resolver.
319  
        @param ctx The execution context that will own this resolver.
320  
    */
320  
    */
321  
    explicit resolver(capy::execution_context& ctx);
321  
    explicit resolver(capy::execution_context& ctx);
322  

322  

323  
    /** Construct a resolver from an executor.
323  
    /** Construct a resolver from an executor.
324  

324  

325  
        The resolver is associated with the executor's context.
325  
        The resolver is associated with the executor's context.
326  

326  

327  
        @param ex The executor whose context will own the resolver.
327  
        @param ex The executor whose context will own the resolver.
328  
    */
328  
    */
329  
    template<class Ex>
329  
    template<class Ex>
330  
        requires (!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
330  
        requires (!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
331  
                 capy::Executor<Ex>
331  
                 capy::Executor<Ex>
332  
    explicit resolver(Ex const& ex)
332  
    explicit resolver(Ex const& ex)
333  
        : resolver(ex.context())
333  
        : resolver(ex.context())
334  
    {
334  
    {
335  
    }
335  
    }
336  

336  

337  
    /** Move constructor.
337  
    /** Move constructor.
338  

338  

339  
        Transfers ownership of the resolver resources.
339  
        Transfers ownership of the resolver resources.
340  

340  

341  
        @param other The resolver to move from.
341  
        @param other The resolver to move from.
342  
    */
342  
    */
343  
    resolver(resolver&& other) noexcept
343  
    resolver(resolver&& other) noexcept
344  
        : io_object(other.context())
344  
        : io_object(other.context())
345  
    {
345  
    {
346  
        impl_ = other.impl_;
346  
        impl_ = other.impl_;
347  
        other.impl_ = nullptr;
347  
        other.impl_ = nullptr;
348  
    }
348  
    }
349  

349  

350  
    /** Move assignment operator.
350  
    /** Move assignment operator.
351  

351  

352  
        Cancels any existing operations and transfers ownership.
352  
        Cancels any existing operations and transfers ownership.
353  
        The source and destination must share the same execution context.
353  
        The source and destination must share the same execution context.
354  

354  

355  
        @param other The resolver to move from.
355  
        @param other The resolver to move from.
356  

356  

357  
        @return Reference to this resolver.
357  
        @return Reference to this resolver.
358  

358  

359  
        @throws std::logic_error if the resolvers have different
359  
        @throws std::logic_error if the resolvers have different
360  
            execution contexts.
360  
            execution contexts.
361  
    */
361  
    */
362  
    resolver& operator=(resolver&& other)
362  
    resolver& operator=(resolver&& other)
363  
    {
363  
    {
364  
        if (this != &other)
364  
        if (this != &other)
365  
        {
365  
        {
366  
            if (ctx_ != other.ctx_)
366  
            if (ctx_ != other.ctx_)
367  
                detail::throw_logic_error(
367  
                detail::throw_logic_error(
368  
                    "cannot move resolver across execution contexts");
368  
                    "cannot move resolver across execution contexts");
369  
            cancel();
369  
            cancel();
370  
            impl_ = other.impl_;
370  
            impl_ = other.impl_;
371  
            other.impl_ = nullptr;
371  
            other.impl_ = nullptr;
372  
        }
372  
        }
373  
        return *this;
373  
        return *this;
374  
    }
374  
    }
375  

375  

376  
    resolver(resolver const&) = delete;
376  
    resolver(resolver const&) = delete;
377  
    resolver& operator=(resolver const&) = delete;
377  
    resolver& operator=(resolver const&) = delete;
378  

378  

379  
    /** Initiate an asynchronous resolve operation.
379  
    /** Initiate an asynchronous resolve operation.
380  

380  

381  
        Resolves the host and service names into a list of endpoints.
381  
        Resolves the host and service names into a list of endpoints.
382  

382  

383  
        @param host A string identifying a location. May be a descriptive
383  
        @param host A string identifying a location. May be a descriptive
384  
            name or a numeric address string.
384  
            name or a numeric address string.
385  

385  

386  
        @param service A string identifying the requested service. This may
386  
        @param service A string identifying the requested service. This may
387  
            be a descriptive name or a numeric string corresponding to a
387  
            be a descriptive name or a numeric string corresponding to a
388  
            port number.
388  
            port number.
389  

389  

390  
        @return An awaitable that completes with `io_result<resolver_results>`.
390  
        @return An awaitable that completes with `io_result<resolver_results>`.
391  

391  

392  
        @par Example
392  
        @par Example
393  
        @code
393  
        @code
394  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
394  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
395  
        @endcode
395  
        @endcode
396  
    */
396  
    */
397  
    auto resolve(
397  
    auto resolve(
398  
        std::string_view host,
398  
        std::string_view host,
399  
        std::string_view service)
399  
        std::string_view service)
400  
    {
400  
    {
401  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
401  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
402  
    }
402  
    }
403  

403  

404  
    /** Initiate an asynchronous resolve operation with flags.
404  
    /** Initiate an asynchronous resolve operation with flags.
405  

405  

406  
        Resolves the host and service names into a list of endpoints.
406  
        Resolves the host and service names into a list of endpoints.
407  

407  

408  
        @param host A string identifying a location.
408  
        @param host A string identifying a location.
409  

409  

410  
        @param service A string identifying the requested service.
410  
        @param service A string identifying the requested service.
411  

411  

412  
        @param flags Flags controlling resolution behavior.
412  
        @param flags Flags controlling resolution behavior.
413  

413  

414  
        @return An awaitable that completes with `io_result<resolver_results>`.
414  
        @return An awaitable that completes with `io_result<resolver_results>`.
415  
    */
415  
    */
416  
    auto resolve(
416  
    auto resolve(
417  
        std::string_view host,
417  
        std::string_view host,
418  
        std::string_view service,
418  
        std::string_view service,
419  
        resolve_flags flags)
419  
        resolve_flags flags)
420  
    {
420  
    {
421  
        return resolve_awaitable(*this, host, service, flags);
421  
        return resolve_awaitable(*this, host, service, flags);
422  
    }
422  
    }
423  

423  

424  
    /** Initiate an asynchronous reverse resolve operation.
424  
    /** Initiate an asynchronous reverse resolve operation.
425  

425  

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

428  

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

430  

431  
        @return An awaitable that completes with
431  
        @return An awaitable that completes with
432  
            `io_result<reverse_resolver_result>`.
432  
            `io_result<reverse_resolver_result>`.
433  

433  

434  
        @par Example
434  
        @par Example
435  
        @code
435  
        @code
436  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
436  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
437  
        auto [ec, result] = co_await r.resolve(ep);
437  
        auto [ec, result] = co_await r.resolve(ep);
438  
        if (!ec)
438  
        if (!ec)
439  
            std::cout << result.host_name() << ":" << result.service_name();
439  
            std::cout << result.host_name() << ":" << result.service_name();
440  
        @endcode
440  
        @endcode
441  
    */
441  
    */
442  
    auto resolve(endpoint const& ep)
442  
    auto resolve(endpoint const& ep)
443  
    {
443  
    {
444  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
444  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
445  
    }
445  
    }
446  

446  

447  
    /** Initiate an asynchronous reverse resolve operation with flags.
447  
    /** Initiate an asynchronous reverse resolve operation with flags.
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  
        @param ep The endpoint to resolve.
452  
        @param ep The endpoint to resolve.
453  

453  

454  
        @param flags Flags controlling resolution behavior. See reverse_flags.
454  
        @param flags Flags controlling resolution behavior. See reverse_flags.
455  

455  

456  
        @return An awaitable that completes with
456  
        @return An awaitable that completes with
457  
            `io_result<reverse_resolver_result>`.
457  
            `io_result<reverse_resolver_result>`.
458  
    */
458  
    */
459  
    auto resolve(endpoint const& ep, reverse_flags flags)
459  
    auto resolve(endpoint const& ep, reverse_flags flags)
460  
    {
460  
    {
461  
        return reverse_resolve_awaitable(*this, ep, flags);
461  
        return reverse_resolve_awaitable(*this, ep, flags);
462  
    }
462  
    }
463  

463  

464  
    /** Cancel any pending asynchronous operations.
464  
    /** Cancel any pending asynchronous operations.
465  

465  

466  
        All outstanding operations complete with `errc::operation_canceled`.
466  
        All outstanding operations complete with `errc::operation_canceled`.
467  
        Check `ec == cond::canceled` for portable comparison.
467  
        Check `ec == cond::canceled` for portable comparison.
468  
    */
468  
    */
469  
    void cancel();
469  
    void cancel();
470  

470  

471  
public:
471  
public:
472  
    struct resolver_impl : io_object_impl
472  
    struct resolver_impl : io_object_impl
473  
    {
473  
    {
474  
        virtual std::coroutine_handle<> resolve(
474  
        virtual std::coroutine_handle<> resolve(
475  
            std::coroutine_handle<>,
475  
            std::coroutine_handle<>,
476  
            capy::executor_ref,
476  
            capy::executor_ref,
477  
            std::string_view host,
477  
            std::string_view host,
478  
            std::string_view service,
478  
            std::string_view service,
479  
            resolve_flags flags,
479  
            resolve_flags flags,
480  
            std::stop_token,
480  
            std::stop_token,
481  
            std::error_code*,
481  
            std::error_code*,
482  
            resolver_results*) = 0;
482  
            resolver_results*) = 0;
483  

483  

484  
        virtual std::coroutine_handle<> reverse_resolve(
484  
        virtual std::coroutine_handle<> reverse_resolve(
485  
            std::coroutine_handle<>,
485  
            std::coroutine_handle<>,
486  
            capy::executor_ref,
486  
            capy::executor_ref,
487  
            endpoint const& ep,
487  
            endpoint const& ep,
488  
            reverse_flags flags,
488  
            reverse_flags flags,
489  
            std::stop_token,
489  
            std::stop_token,
490  
            std::error_code*,
490  
            std::error_code*,
491  
            reverse_resolver_result*) = 0;
491  
            reverse_resolver_result*) = 0;
492  

492  

493  
        virtual void cancel() noexcept = 0;
493  
        virtual void cancel() noexcept = 0;
494  
    };
494  
    };
495  

495  

496  
private:
496  
private:
497  
    inline resolver_impl& get() const noexcept
497  
    inline resolver_impl& get() const noexcept
498  
    {
498  
    {
499  
        return *static_cast<resolver_impl*>(impl_);
499  
        return *static_cast<resolver_impl*>(impl_);
500  
    }
500  
    }
501  
};
501  
};
502  

502  

503  
} // namespace boost::corosio
503  
} // namespace boost::corosio
504  

504  

505  
#endif
505  
#endif