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/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/ex/io_env.hpp>
21  
#include <boost/capy/ex/io_env.hpp>
22  
#include <boost/capy/concept/executor.hpp>
22  
#include <boost/capy/concept/executor.hpp>
23  

23  

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

25  

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

34  

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

36  

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

38  

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

45  

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

49  

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

53  

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

57  

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

62  

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

66  

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

70  

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

80  

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

89  

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

99  

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

108  

109  
//------------------------------------------------------------------------------
109  
//------------------------------------------------------------------------------
110  

110  

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

112  

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

119  

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

122  

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

125  

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

128  

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

132  

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

142  

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

151  

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

161  

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

170  

171  
//------------------------------------------------------------------------------
171  
//------------------------------------------------------------------------------
172  

172  

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

174  

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

178  

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

183  

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

188  

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

193  

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

198  

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

201  

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

217  

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

229  

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

234  

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

241  

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

250  

251  
    struct reverse_resolve_awaitable
251  
    struct reverse_resolve_awaitable
252  
    {
252  
    {
253  
        resolver& r_;
253  
        resolver& r_;
254  
        endpoint ep_;
254  
        endpoint ep_;
255  
        reverse_flags flags_;
255  
        reverse_flags flags_;
256  
        std::stop_token token_;
256  
        std::stop_token token_;
257  
        mutable std::error_code ec_;
257  
        mutable std::error_code ec_;
258  
        mutable reverse_resolver_result result_;
258  
        mutable reverse_resolver_result result_;
259  

259  

260  
        reverse_resolve_awaitable(
260  
        reverse_resolve_awaitable(
261  
            resolver& r,
261  
            resolver& r,
262  
            endpoint const& ep,
262  
            endpoint const& ep,
263  
            reverse_flags flags) noexcept
263  
            reverse_flags flags) noexcept
264  
            : r_(r)
264  
            : r_(r)
265  
            , ep_(ep)
265  
            , ep_(ep)
266  
            , flags_(flags)
266  
            , flags_(flags)
267  
        {
267  
        {
268  
        }
268  
        }
269  

269  

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

274  

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

281  

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

290  

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

293  

294  
        Cancels any pending operations.
294  
        Cancels any pending operations.
295  
    */
295  
    */
296  
    ~resolver();
296  
    ~resolver();
297  

297  

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

299  

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

303  

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

305  

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

307  

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

317  

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

319  

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

321  

322  
        @param other The resolver to move from.
322  
        @param other The resolver to move from.
323  
    */
323  
    */
324  
    resolver(resolver&& other) noexcept
324  
    resolver(resolver&& other) noexcept
325  
        : io_object(other.context())
325  
        : io_object(other.context())
326  
    {
326  
    {
327  
        impl_ = other.impl_;
327  
        impl_ = other.impl_;
328  
        other.impl_ = nullptr;
328  
        other.impl_ = nullptr;
329  
    }
329  
    }
330  

330  

331  
    /** Move assignment operator.
331  
    /** Move assignment operator.
332  

332  

333  
        Cancels any existing operations and transfers ownership.
333  
        Cancels any existing operations and transfers ownership.
334  
        The source and destination must share the same execution context.
334  
        The source and destination must share the same execution context.
335  

335  

336  
        @param other The resolver to move from.
336  
        @param other The resolver to move from.
337  

337  

338  
        @return Reference to this resolver.
338  
        @return Reference to this resolver.
339  

339  

340  
        @throws std::logic_error if the resolvers have different
340  
        @throws std::logic_error if the resolvers have different
341  
            execution contexts.
341  
            execution contexts.
342  
    */
342  
    */
343  
    resolver& operator=(resolver&& other)
343  
    resolver& operator=(resolver&& other)
344  
    {
344  
    {
345  
        if (this != &other)
345  
        if (this != &other)
346  
        {
346  
        {
347  
            if (ctx_ != other.ctx_)
347  
            if (ctx_ != other.ctx_)
348  
                detail::throw_logic_error(
348  
                detail::throw_logic_error(
349  
                    "cannot move resolver across execution contexts");
349  
                    "cannot move resolver across execution contexts");
350  
            cancel();
350  
            cancel();
351  
            impl_ = other.impl_;
351  
            impl_ = other.impl_;
352  
            other.impl_ = nullptr;
352  
            other.impl_ = nullptr;
353  
        }
353  
        }
354  
        return *this;
354  
        return *this;
355  
    }
355  
    }
356  

356  

357  
    resolver(resolver const&) = delete;
357  
    resolver(resolver const&) = delete;
358  
    resolver& operator=(resolver const&) = delete;
358  
    resolver& operator=(resolver const&) = delete;
359  

359  

360  
    /** Initiate an asynchronous resolve operation.
360  
    /** Initiate an asynchronous resolve operation.
361  

361  

362  
        Resolves the host and service names into a list of endpoints.
362  
        Resolves the host and service names into a list of endpoints.
363  

363  

364  
        @param host A string identifying a location. May be a descriptive
364  
        @param host A string identifying a location. May be a descriptive
365  
            name or a numeric address string.
365  
            name or a numeric address string.
366  

366  

367  
        @param service A string identifying the requested service. This may
367  
        @param service A string identifying the requested service. This may
368  
            be a descriptive name or a numeric string corresponding to a
368  
            be a descriptive name or a numeric string corresponding to a
369  
            port number.
369  
            port number.
370  

370  

371  
        @return An awaitable that completes with `io_result<resolver_results>`.
371  
        @return An awaitable that completes with `io_result<resolver_results>`.
372  

372  

373  
        @par Example
373  
        @par Example
374  
        @code
374  
        @code
375  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
375  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
376  
        @endcode
376  
        @endcode
377  
    */
377  
    */
378  
    auto resolve(
378  
    auto resolve(
379  
        std::string_view host,
379  
        std::string_view host,
380  
        std::string_view service)
380  
        std::string_view service)
381  
    {
381  
    {
382  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
382  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
383  
    }
383  
    }
384  

384  

385  
    /** Initiate an asynchronous resolve operation with flags.
385  
    /** Initiate an asynchronous resolve operation with flags.
386  

386  

387  
        Resolves the host and service names into a list of endpoints.
387  
        Resolves the host and service names into a list of endpoints.
388  

388  

389  
        @param host A string identifying a location.
389  
        @param host A string identifying a location.
390  

390  

391  
        @param service A string identifying the requested service.
391  
        @param service A string identifying the requested service.
392  

392  

393  
        @param flags Flags controlling resolution behavior.
393  
        @param flags Flags controlling resolution behavior.
394  

394  

395  
        @return An awaitable that completes with `io_result<resolver_results>`.
395  
        @return An awaitable that completes with `io_result<resolver_results>`.
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  
        resolve_flags flags)
400  
        resolve_flags flags)
401  
    {
401  
    {
402  
        return resolve_awaitable(*this, host, service, flags);
402  
        return resolve_awaitable(*this, host, service, flags);
403  
    }
403  
    }
404  

404  

405  
    /** Initiate an asynchronous reverse resolve operation.
405  
    /** Initiate an asynchronous reverse resolve operation.
406  

406  

407  
        Resolves an endpoint into a hostname and service name using
407  
        Resolves an endpoint into a hostname and service name using
408  
        reverse DNS lookup (PTR record query).
408  
        reverse DNS lookup (PTR record query).
409  

409  

410  
        @param ep The endpoint to resolve.
410  
        @param ep The endpoint to resolve.
411  

411  

412  
        @return An awaitable that completes with
412  
        @return An awaitable that completes with
413  
            `io_result<reverse_resolver_result>`.
413  
            `io_result<reverse_resolver_result>`.
414  

414  

415  
        @par Example
415  
        @par Example
416  
        @code
416  
        @code
417  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
417  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
418  
        auto [ec, result] = co_await r.resolve(ep);
418  
        auto [ec, result] = co_await r.resolve(ep);
419  
        if (!ec)
419  
        if (!ec)
420  
            std::cout << result.host_name() << ":" << result.service_name();
420  
            std::cout << result.host_name() << ":" << result.service_name();
421  
        @endcode
421  
        @endcode
422  
    */
422  
    */
423  
    auto resolve(endpoint const& ep)
423  
    auto resolve(endpoint const& ep)
424  
    {
424  
    {
425  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
425  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
426  
    }
426  
    }
427  

427  

428  
    /** Initiate an asynchronous reverse resolve operation with flags.
428  
    /** Initiate an asynchronous reverse resolve operation with flags.
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  
        @param flags Flags controlling resolution behavior. See reverse_flags.
435  
        @param flags Flags controlling resolution behavior. See reverse_flags.
436  

436  

437  
        @return An awaitable that completes with
437  
        @return An awaitable that completes with
438  
            `io_result<reverse_resolver_result>`.
438  
            `io_result<reverse_resolver_result>`.
439  
    */
439  
    */
440  
    auto resolve(endpoint const& ep, reverse_flags flags)
440  
    auto resolve(endpoint const& ep, reverse_flags flags)
441  
    {
441  
    {
442  
        return reverse_resolve_awaitable(*this, ep, flags);
442  
        return reverse_resolve_awaitable(*this, ep, flags);
443  
    }
443  
    }
444  

444  

445  
    /** Cancel any pending asynchronous operations.
445  
    /** Cancel any pending asynchronous operations.
446  

446  

447  
        All outstanding operations complete with `errc::operation_canceled`.
447  
        All outstanding operations complete with `errc::operation_canceled`.
448  
        Check `ec == cond::canceled` for portable comparison.
448  
        Check `ec == cond::canceled` for portable comparison.
449  
    */
449  
    */
450  
    void cancel();
450  
    void cancel();
451  

451  

452  
public:
452  
public:
453  
    struct resolver_impl : io_object_impl
453  
    struct resolver_impl : io_object_impl
454  
    {
454  
    {
455  
        virtual std::coroutine_handle<> resolve(
455  
        virtual std::coroutine_handle<> resolve(
456  
            std::coroutine_handle<>,
456  
            std::coroutine_handle<>,
457  
            capy::executor_ref,
457  
            capy::executor_ref,
458  
            std::string_view host,
458  
            std::string_view host,
459  
            std::string_view service,
459  
            std::string_view service,
460  
            resolve_flags flags,
460  
            resolve_flags flags,
461  
            std::stop_token,
461  
            std::stop_token,
462  
            std::error_code*,
462  
            std::error_code*,
463  
            resolver_results*) = 0;
463  
            resolver_results*) = 0;
464  

464  

465  
        virtual std::coroutine_handle<> reverse_resolve(
465  
        virtual std::coroutine_handle<> reverse_resolve(
466  
            std::coroutine_handle<>,
466  
            std::coroutine_handle<>,
467  
            capy::executor_ref,
467  
            capy::executor_ref,
468  
            endpoint const& ep,
468  
            endpoint const& ep,
469  
            reverse_flags flags,
469  
            reverse_flags flags,
470  
            std::stop_token,
470  
            std::stop_token,
471  
            std::error_code*,
471  
            std::error_code*,
472  
            reverse_resolver_result*) = 0;
472  
            reverse_resolver_result*) = 0;
473  

473  

474  
        virtual void cancel() noexcept = 0;
474  
        virtual void cancel() noexcept = 0;
475  
    };
475  
    };
476  

476  

477  
private:
477  
private:
478  
    inline resolver_impl& get() const noexcept
478  
    inline resolver_impl& get() const noexcept
479  
    {
479  
    {
480  
        return *static_cast<resolver_impl*>(impl_);
480  
        return *static_cast<resolver_impl*>(impl_);
481  
    }
481  
    }
482  
};
482  
};
483  

483  

484  
} // namespace boost::corosio
484  
} // namespace boost::corosio
485  

485  

486  
#endif
486  
#endif