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  
            r_.get().resolve(h, ex, host_, service_, flags_, token_, &ec_, &results_);
246  
            r_.get().resolve(h, ex, host_, service_, flags_, token_, &ec_, &results_);
247  
            return std::noop_coroutine();
247  
            return std::noop_coroutine();
248  
        }
248  
        }
249  

249  

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

261  

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

270  

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

280  

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

285  

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

292  

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

301  

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

313  

314  
public:
314  
public:
315  
    /** Destructor.
315  
    /** Destructor.
316  

316  

317  
        Cancels any pending operations.
317  
        Cancels any pending operations.
318  
    */
318  
    */
319  
    ~resolver();
319  
    ~resolver();
320  

320  

321  
    /** Construct a resolver from an execution context.
321  
    /** Construct a resolver from an execution context.
322  

322  

323  
        @param ctx The execution context that will own this resolver.
323  
        @param ctx The execution context that will own this resolver.
324  
    */
324  
    */
325  
    explicit resolver(capy::execution_context& ctx);
325  
    explicit resolver(capy::execution_context& ctx);
326  

326  

327  
    /** Construct a resolver from an executor.
327  
    /** Construct a resolver from an executor.
328  

328  

329  
        The resolver is associated with the executor's context.
329  
        The resolver is associated with the executor's context.
330  

330  

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

340  

341  
    /** Move constructor.
341  
    /** Move constructor.
342  

342  

343  
        Transfers ownership of the resolver resources.
343  
        Transfers ownership of the resolver resources.
344  

344  

345  
        @param other The resolver to move from.
345  
        @param other The resolver to move from.
346  
    */
346  
    */
347  
    resolver(resolver&& other) noexcept
347  
    resolver(resolver&& other) noexcept
348  
        : io_object(other.context())
348  
        : io_object(other.context())
349  
    {
349  
    {
350  
        impl_ = other.impl_;
350  
        impl_ = other.impl_;
351  
        other.impl_ = nullptr;
351  
        other.impl_ = nullptr;
352  
    }
352  
    }
353  

353  

354  
    /** Move assignment operator.
354  
    /** Move assignment operator.
355  

355  

356  
        Cancels any existing operations and transfers ownership.
356  
        Cancels any existing operations and transfers ownership.
357  
        The source and destination must share the same execution context.
357  
        The source and destination must share the same execution context.
358  

358  

359  
        @param other The resolver to move from.
359  
        @param other The resolver to move from.
360  

360  

361  
        @return Reference to this resolver.
361  
        @return Reference to this resolver.
362  

362  

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

379  

380  
    resolver(resolver const&) = delete;
380  
    resolver(resolver const&) = delete;
381  
    resolver& operator=(resolver const&) = delete;
381  
    resolver& operator=(resolver const&) = delete;
382  

382  

383  
    /** Initiate an asynchronous resolve operation.
383  
    /** Initiate an asynchronous resolve operation.
384  

384  

385  
        Resolves the host and service names into a list of endpoints.
385  
        Resolves the host and service names into a list of endpoints.
386  

386  

387  
        @param host A string identifying a location. May be a descriptive
387  
        @param host A string identifying a location. May be a descriptive
388  
            name or a numeric address string.
388  
            name or a numeric address string.
389  

389  

390  
        @param service A string identifying the requested service. This may
390  
        @param service A string identifying the requested service. This may
391  
            be a descriptive name or a numeric string corresponding to a
391  
            be a descriptive name or a numeric string corresponding to a
392  
            port number.
392  
            port number.
393  

393  

394  
        @return An awaitable that completes with `io_result<resolver_results>`.
394  
        @return An awaitable that completes with `io_result<resolver_results>`.
395  

395  

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

407  

408  
    /** Initiate an asynchronous resolve operation with flags.
408  
    /** Initiate an asynchronous resolve operation with flags.
409  

409  

410  
        Resolves the host and service names into a list of endpoints.
410  
        Resolves the host and service names into a list of endpoints.
411  

411  

412  
        @param host A string identifying a location.
412  
        @param host A string identifying a location.
413  

413  

414  
        @param service A string identifying the requested service.
414  
        @param service A string identifying the requested service.
415  

415  

416  
        @param flags Flags controlling resolution behavior.
416  
        @param flags Flags controlling resolution behavior.
417  

417  

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

427  

428  
    /** Initiate an asynchronous reverse resolve operation.
428  
    /** Initiate an asynchronous reverse resolve operation.
429  

429  

430  
        Resolves an endpoint into a hostname and service name using
430  
        Resolves an endpoint into a hostname and service name using
431  
        reverse DNS lookup (PTR record query).
431  
        reverse DNS lookup (PTR record query).
432  

432  

433  
        @param ep The endpoint to resolve.
433  
        @param ep The endpoint to resolve.
434  

434  

435  
        @return An awaitable that completes with
435  
        @return An awaitable that completes with
436  
            `io_result<reverse_resolver_result>`.
436  
            `io_result<reverse_resolver_result>`.
437  

437  

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

450  

451  
    /** Initiate an asynchronous reverse resolve operation with flags.
451  
    /** Initiate an asynchronous reverse resolve operation with flags.
452  

452  

453  
        Resolves an endpoint into a hostname and service name using
453  
        Resolves an endpoint into a hostname and service name using
454  
        reverse DNS lookup (PTR record query).
454  
        reverse DNS lookup (PTR record query).
455  

455  

456  
        @param ep The endpoint to resolve.
456  
        @param ep The endpoint to resolve.
457  

457  

458  
        @param flags Flags controlling resolution behavior. See reverse_flags.
458  
        @param flags Flags controlling resolution behavior. See reverse_flags.
459  

459  

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

467  

468  
    /** Cancel any pending asynchronous operations.
468  
    /** Cancel any pending asynchronous operations.
469  

469  

470  
        All outstanding operations complete with `errc::operation_canceled`.
470  
        All outstanding operations complete with `errc::operation_canceled`.
471  
        Check `ec == cond::canceled` for portable comparison.
471  
        Check `ec == cond::canceled` for portable comparison.
472  
    */
472  
    */
473  
    void cancel();
473  
    void cancel();
474  

474  

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

487  

488  
        virtual void reverse_resolve(
488  
        virtual void reverse_resolve(
489  
            std::coroutine_handle<>,
489  
            std::coroutine_handle<>,
490  
            capy::executor_ref,
490  
            capy::executor_ref,
491  
            endpoint const& ep,
491  
            endpoint const& ep,
492  
            reverse_flags flags,
492  
            reverse_flags flags,
493  
            std::stop_token,
493  
            std::stop_token,
494  
            std::error_code*,
494  
            std::error_code*,
495  
            reverse_resolver_result*) = 0;
495  
            reverse_resolver_result*) = 0;
496  

496  

497  
        virtual void cancel() noexcept = 0;
497  
        virtual void cancel() noexcept = 0;
498  
    };
498  
    };
499  

499  

500  
private:
500  
private:
501  
    inline resolver_impl& get() const noexcept
501  
    inline resolver_impl& get() const noexcept
502  
    {
502  
    {
503  
        return *static_cast<resolver_impl*>(impl_);
503  
        return *static_cast<resolver_impl*>(impl_);
504  
    }
504  
    }
505  
};
505  
};
506  

506  

507  
} // namespace boost::corosio
507  
} // namespace boost::corosio
508  

508  

509  
#endif
509  
#endif