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

10  

11  
#ifndef BOOST_COROSIO_RESOLVER_HPP
11  
#ifndef BOOST_COROSIO_RESOLVER_HPP
12  
#define BOOST_COROSIO_RESOLVER_HPP
12  
#define BOOST_COROSIO_RESOLVER_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/endpoint.hpp>
15  
#include <boost/corosio/endpoint.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
16  
#include <boost/corosio/io/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 <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 resolve_flags
71  
inline resolve_flags
72  
operator|(resolve_flags a, resolve_flags b) noexcept
72  
operator|(resolve_flags a, resolve_flags b) noexcept
73  
{
73  
{
74  
    return static_cast<resolve_flags>(
74  
    return static_cast<resolve_flags>(
75  
        static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
75  
        static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
76  
}
76  
}
77  

77  

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

85  

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

93  

94  
/** Intersect two resolve_flags. */
94  
/** Intersect two resolve_flags. */
95  
inline resolve_flags&
95  
inline resolve_flags&
96  
operator&=(resolve_flags& a, resolve_flags b) noexcept
96  
operator&=(resolve_flags& a, resolve_flags b) noexcept
97  
{
97  
{
98  
    a = a & b;
98  
    a = a & b;
99  
    return a;
99  
    return a;
100  
}
100  
}
101  

101  

102  
/** Bitmask flags for reverse resolver queries.
102  
/** Bitmask flags for reverse resolver queries.
103  

103  

104  
    These flags correspond to the flags parameter of getnameinfo.
104  
    These flags correspond to the flags parameter of getnameinfo.
105  
*/
105  
*/
106  
enum class reverse_flags : unsigned int
106  
enum class reverse_flags : unsigned int
107  
{
107  
{
108  
    /// No flags.
108  
    /// No flags.
109  
    none = 0,
109  
    none = 0,
110  

110  

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

113  

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

116  

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

119  

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

123  

124  
/** Combine two reverse_flags. */
124  
/** Combine two reverse_flags. */
125  
inline reverse_flags
125  
inline reverse_flags
126  
operator|(reverse_flags a, reverse_flags b) noexcept
126  
operator|(reverse_flags a, reverse_flags b) noexcept
127  
{
127  
{
128  
    return static_cast<reverse_flags>(
128  
    return static_cast<reverse_flags>(
129  
        static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
129  
        static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
130  
}
130  
}
131  

131  

132  
/** Combine two reverse_flags. */
132  
/** Combine two reverse_flags. */
133  
inline reverse_flags&
133  
inline reverse_flags&
134  
operator|=(reverse_flags& a, reverse_flags b) noexcept
134  
operator|=(reverse_flags& a, reverse_flags b) noexcept
135  
{
135  
{
136  
    a = a | b;
136  
    a = a | b;
137  
    return a;
137  
    return a;
138  
}
138  
}
139  

139  

140  
/** Intersect two reverse_flags. */
140  
/** Intersect two reverse_flags. */
141  
inline reverse_flags
141  
inline reverse_flags
142  
operator&(reverse_flags a, reverse_flags b) noexcept
142  
operator&(reverse_flags a, reverse_flags b) noexcept
143  
{
143  
{
144  
    return static_cast<reverse_flags>(
144  
    return static_cast<reverse_flags>(
145  
        static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
145  
        static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
146  
}
146  
}
147  

147  

148  
/** Intersect two reverse_flags. */
148  
/** Intersect two reverse_flags. */
149  
inline reverse_flags&
149  
inline reverse_flags&
150  
operator&=(reverse_flags& a, reverse_flags b) noexcept
150  
operator&=(reverse_flags& a, reverse_flags b) noexcept
151  
{
151  
{
152  
    a = a & b;
152  
    a = a & b;
153  
    return a;
153  
    return a;
154  
}
154  
}
155  

155  

156  
/** An asynchronous DNS resolver for coroutine I/O.
156  
/** An asynchronous DNS resolver for coroutine I/O.
157  

157  

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

161  

162  
    @par Thread Safety
162  
    @par Thread Safety
163  
    Distinct objects: Safe.@n
163  
    Distinct objects: Safe.@n
164  
    Shared objects: Unsafe. A resolver must not have concurrent resolve
164  
    Shared objects: Unsafe. A resolver must not have concurrent resolve
165  
    operations.
165  
    operations.
166  

166  

167  
    @par Semantics
167  
    @par Semantics
168  
    Wraps platform DNS resolution (getaddrinfo/getnameinfo).
168  
    Wraps platform DNS resolution (getaddrinfo/getnameinfo).
169  
    Operations dispatch to OS resolver APIs via the io_context
169  
    Operations dispatch to OS resolver APIs via the io_context
170  
    thread pool.
170  
    thread pool.
171  

171  

172  
    @par Example
172  
    @par Example
173  
    @code
173  
    @code
174  
    io_context ioc;
174  
    io_context ioc;
175  
    resolver r(ioc);
175  
    resolver r(ioc);
176  

176  

177  
    // Using structured bindings
177  
    // Using structured bindings
178  
    auto [ec, results] = co_await r.resolve("www.example.com", "https");
178  
    auto [ec, results] = co_await r.resolve("www.example.com", "https");
179  
    if (ec)
179  
    if (ec)
180  
        co_return;
180  
        co_return;
181  

181  

182  
    for (auto const& entry : results)
182  
    for (auto const& entry : results)
183  
        std::cout << entry.get_endpoint().port() << std::endl;
183  
        std::cout << entry.get_endpoint().port() << std::endl;
184  

184  

185  
    // Or using exceptions
185  
    // Or using exceptions
186  
    auto results = (co_await r.resolve("www.example.com", "https")).value();
186  
    auto results = (co_await r.resolve("www.example.com", "https")).value();
187  
    @endcode
187  
    @endcode
188  
*/
188  
*/
189  
class BOOST_COROSIO_DECL resolver : public io_object
189  
class BOOST_COROSIO_DECL resolver : public io_object
190  
{
190  
{
191  
    struct resolve_awaitable
191  
    struct resolve_awaitable
192  
    {
192  
    {
193  
        resolver& r_;
193  
        resolver& r_;
194  
        std::string host_;
194  
        std::string host_;
195  
        std::string service_;
195  
        std::string service_;
196  
        resolve_flags flags_;
196  
        resolve_flags flags_;
197  
        std::stop_token token_;
197  
        std::stop_token token_;
198  
        mutable std::error_code ec_;
198  
        mutable std::error_code ec_;
199  
        mutable resolver_results results_;
199  
        mutable resolver_results results_;
200  

200  

201  
        resolve_awaitable(
201  
        resolve_awaitable(
202  
            resolver& r,
202  
            resolver& r,
203  
            std::string_view host,
203  
            std::string_view host,
204  
            std::string_view service,
204  
            std::string_view service,
205  
            resolve_flags flags) noexcept
205  
            resolve_flags flags) noexcept
206  
            : r_(r)
206  
            : r_(r)
207  
            , host_(host)
207  
            , host_(host)
208  
            , service_(service)
208  
            , service_(service)
209  
            , flags_(flags)
209  
            , flags_(flags)
210  
        {
210  
        {
211  
        }
211  
        }
212  

212  

213  
        bool await_ready() const noexcept
213  
        bool await_ready() const noexcept
214  
        {
214  
        {
215  
            return token_.stop_requested();
215  
            return token_.stop_requested();
216  
        }
216  
        }
217  

217  

218  
        capy::io_result<resolver_results> await_resume() const noexcept
218  
        capy::io_result<resolver_results> await_resume() const noexcept
219  
        {
219  
        {
220  
            if (token_.stop_requested())
220  
            if (token_.stop_requested())
221  
                return {make_error_code(std::errc::operation_canceled), {}};
221  
                return {make_error_code(std::errc::operation_canceled), {}};
222  
            return {ec_, std::move(results_)};
222  
            return {ec_, std::move(results_)};
223  
        }
223  
        }
224  

224  

225  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
225  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
226  
            -> std::coroutine_handle<>
226  
            -> std::coroutine_handle<>
227  
        {
227  
        {
228  
            token_ = env->stop_token;
228  
            token_ = env->stop_token;
229  
            return r_.get().resolve(
229  
            return r_.get().resolve(
230  
                h, env->executor, host_, service_, flags_, token_, &ec_,
230  
                h, env->executor, host_, service_, flags_, token_, &ec_,
231  
                &results_);
231  
                &results_);
232  
        }
232  
        }
233  
    };
233  
    };
234  

234  

235  
    struct reverse_resolve_awaitable
235  
    struct reverse_resolve_awaitable
236  
    {
236  
    {
237  
        resolver& r_;
237  
        resolver& r_;
238  
        endpoint ep_;
238  
        endpoint ep_;
239  
        reverse_flags flags_;
239  
        reverse_flags flags_;
240  
        std::stop_token token_;
240  
        std::stop_token token_;
241  
        mutable std::error_code ec_;
241  
        mutable std::error_code ec_;
242  
        mutable reverse_resolver_result result_;
242  
        mutable reverse_resolver_result result_;
243  

243  

244  
        reverse_resolve_awaitable(
244  
        reverse_resolve_awaitable(
245  
            resolver& r, endpoint const& ep, reverse_flags flags) noexcept
245  
            resolver& r, endpoint const& ep, reverse_flags flags) noexcept
246  
            : r_(r)
246  
            : r_(r)
247  
            , ep_(ep)
247  
            , ep_(ep)
248  
            , flags_(flags)
248  
            , flags_(flags)
249  
        {
249  
        {
250  
        }
250  
        }
251  

251  

252  
        bool await_ready() const noexcept
252  
        bool await_ready() const noexcept
253  
        {
253  
        {
254  
            return token_.stop_requested();
254  
            return token_.stop_requested();
255  
        }
255  
        }
256  

256  

257  
        capy::io_result<reverse_resolver_result> await_resume() const noexcept
257  
        capy::io_result<reverse_resolver_result> await_resume() const noexcept
258  
        {
258  
        {
259  
            if (token_.stop_requested())
259  
            if (token_.stop_requested())
260  
                return {make_error_code(std::errc::operation_canceled), {}};
260  
                return {make_error_code(std::errc::operation_canceled), {}};
261  
            return {ec_, std::move(result_)};
261  
            return {ec_, std::move(result_)};
262  
        }
262  
        }
263  

263  

264  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
264  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
265  
            -> std::coroutine_handle<>
265  
            -> std::coroutine_handle<>
266  
        {
266  
        {
267  
            token_ = env->stop_token;
267  
            token_ = env->stop_token;
268  
            return r_.get().reverse_resolve(
268  
            return r_.get().reverse_resolve(
269  
                h, env->executor, ep_, flags_, token_, &ec_, &result_);
269  
                h, env->executor, ep_, flags_, token_, &ec_, &result_);
270  
        }
270  
        }
271  
    };
271  
    };
272  

272  

273  
public:
273  
public:
274  
    /** Destructor.
274  
    /** Destructor.
275  

275  

276  
        Cancels any pending operations.
276  
        Cancels any pending operations.
277  
    */
277  
    */
278  
    ~resolver() override;
278  
    ~resolver() override;
279  

279  

280  
    /** Construct a resolver from an execution context.
280  
    /** Construct a resolver from an execution context.
281  

281  

282  
        @param ctx The execution context that will own this resolver.
282  
        @param ctx The execution context that will own this resolver.
283  
    */
283  
    */
284  
    explicit resolver(capy::execution_context& ctx);
284  
    explicit resolver(capy::execution_context& ctx);
285  

285  

286  
    /** Construct a resolver from an executor.
286  
    /** Construct a resolver from an executor.
287  

287  

288  
        The resolver is associated with the executor's context.
288  
        The resolver is associated with the executor's context.
289  

289  

290  
        @param ex The executor whose context will own the resolver.
290  
        @param ex The executor whose context will own the resolver.
291  
    */
291  
    */
292  
    template<class Ex>
292  
    template<class Ex>
293  
        requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
293  
        requires(!std::same_as<std::remove_cvref_t<Ex>, resolver>) &&
294  
        capy::Executor<Ex>
294  
        capy::Executor<Ex>
295  
    explicit resolver(Ex const& ex) : resolver(ex.context())
295  
    explicit resolver(Ex const& ex) : resolver(ex.context())
296  
    {
296  
    {
297  
    }
297  
    }
298  

298  

299  
    /** Move constructor.
299  
    /** Move constructor.
300  

300  

301  
        Transfers ownership of the resolver resources.
301  
        Transfers ownership of the resolver resources.
302  

302  

303  
        @param other The resolver to move from.
303  
        @param other The resolver to move from.
304  
    */
304  
    */
305  
    resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
305  
    resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
306  

306  

307  
    /** Move assignment operator.
307  
    /** Move assignment operator.
308  

308  

309  
        Cancels any existing operations and transfers ownership.
309  
        Cancels any existing operations and transfers ownership.
310  
        The source and destination must share the same execution context.
310  
        The source and destination must share the same execution context.
311  

311  

312  
        @param other The resolver to move from.
312  
        @param other The resolver to move from.
313  

313  

314  
        @return Reference to this resolver.
314  
        @return Reference to this resolver.
315  
    */
315  
    */
316  
    resolver& operator=(resolver&& other) noexcept
316  
    resolver& operator=(resolver&& other) noexcept
317  
    {
317  
    {
318  
        if (this != &other)
318  
        if (this != &other)
319  
            h_ = std::move(other.h_);
319  
            h_ = std::move(other.h_);
320  
        return *this;
320  
        return *this;
321  
    }
321  
    }
322  

322  

323  
    resolver(resolver const&)            = delete;
323  
    resolver(resolver const&)            = delete;
324  
    resolver& operator=(resolver const&) = delete;
324  
    resolver& operator=(resolver const&) = delete;
325  

325  

326  
    /** Initiate an asynchronous resolve operation.
326  
    /** Initiate an asynchronous resolve operation.
327  

327  

328  
        Resolves the host and service names into a list of endpoints.
328  
        Resolves the host and service names into a list of endpoints.
329  

329  

330  
        @param host A string identifying a location. May be a descriptive
330  
        @param host A string identifying a location. May be a descriptive
331  
            name or a numeric address string.
331  
            name or a numeric address string.
332  

332  

333  
        @param service A string identifying the requested service. This may
333  
        @param service A string identifying the requested service. This may
334  
            be a descriptive name or a numeric string corresponding to a
334  
            be a descriptive name or a numeric string corresponding to a
335  
            port number.
335  
            port number.
336  

336  

337  
        @return An awaitable that completes with `io_result<resolver_results>`.
337  
        @return An awaitable that completes with `io_result<resolver_results>`.
338  

338  

339  
        @par Example
339  
        @par Example
340  
        @code
340  
        @code
341  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
341  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
342  
        @endcode
342  
        @endcode
343  
    */
343  
    */
344  
    auto resolve(std::string_view host, std::string_view service)
344  
    auto resolve(std::string_view host, std::string_view service)
345  
    {
345  
    {
346  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
346  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
347  
    }
347  
    }
348  

348  

349  
    /** Initiate an asynchronous resolve operation with flags.
349  
    /** Initiate an asynchronous resolve operation with flags.
350  

350  

351  
        Resolves the host and service names into a list of endpoints.
351  
        Resolves the host and service names into a list of endpoints.
352  

352  

353  
        @param host A string identifying a location.
353  
        @param host A string identifying a location.
354  

354  

355  
        @param service A string identifying the requested service.
355  
        @param service A string identifying the requested service.
356  

356  

357  
        @param flags Flags controlling resolution behavior.
357  
        @param flags Flags controlling resolution behavior.
358  

358  

359  
        @return An awaitable that completes with `io_result<resolver_results>`.
359  
        @return An awaitable that completes with `io_result<resolver_results>`.
360  
    */
360  
    */
361  
    auto resolve(
361  
    auto resolve(
362  
        std::string_view host, std::string_view service, resolve_flags flags)
362  
        std::string_view host, std::string_view service, resolve_flags flags)
363  
    {
363  
    {
364  
        return resolve_awaitable(*this, host, service, flags);
364  
        return resolve_awaitable(*this, host, service, flags);
365  
    }
365  
    }
366  

366  

367  
    /** Initiate an asynchronous reverse resolve operation.
367  
    /** Initiate an asynchronous reverse resolve operation.
368  

368  

369  
        Resolves an endpoint into a hostname and service name using
369  
        Resolves an endpoint into a hostname and service name using
370  
        reverse DNS lookup (PTR record query).
370  
        reverse DNS lookup (PTR record query).
371  

371  

372  
        @param ep The endpoint to resolve.
372  
        @param ep The endpoint to resolve.
373  

373  

374  
        @return An awaitable that completes with
374  
        @return An awaitable that completes with
375  
            `io_result<reverse_resolver_result>`.
375  
            `io_result<reverse_resolver_result>`.
376  

376  

377  
        @par Example
377  
        @par Example
378  
        @code
378  
        @code
379  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
379  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
380  
        auto [ec, result] = co_await r.resolve(ep);
380  
        auto [ec, result] = co_await r.resolve(ep);
381  
        if (!ec)
381  
        if (!ec)
382  
            std::cout << result.host_name() << ":" << result.service_name();
382  
            std::cout << result.host_name() << ":" << result.service_name();
383  
        @endcode
383  
        @endcode
384  
    */
384  
    */
385  
    auto resolve(endpoint const& ep)
385  
    auto resolve(endpoint const& ep)
386  
    {
386  
    {
387  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
387  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
388  
    }
388  
    }
389  

389  

390  
    /** Initiate an asynchronous reverse resolve operation with flags.
390  
    /** Initiate an asynchronous reverse resolve operation with flags.
391  

391  

392  
        Resolves an endpoint into a hostname and service name using
392  
        Resolves an endpoint into a hostname and service name using
393  
        reverse DNS lookup (PTR record query).
393  
        reverse DNS lookup (PTR record query).
394  

394  

395  
        @param ep The endpoint to resolve.
395  
        @param ep The endpoint to resolve.
396  

396  

397  
        @param flags Flags controlling resolution behavior. See reverse_flags.
397  
        @param flags Flags controlling resolution behavior. See reverse_flags.
398  

398  

399  
        @return An awaitable that completes with
399  
        @return An awaitable that completes with
400  
            `io_result<reverse_resolver_result>`.
400  
            `io_result<reverse_resolver_result>`.
401  
    */
401  
    */
402  
    auto resolve(endpoint const& ep, reverse_flags flags)
402  
    auto resolve(endpoint const& ep, reverse_flags flags)
403  
    {
403  
    {
404  
        return reverse_resolve_awaitable(*this, ep, flags);
404  
        return reverse_resolve_awaitable(*this, ep, flags);
405  
    }
405  
    }
406  

406  

407  
    /** Cancel any pending asynchronous operations.
407  
    /** Cancel any pending asynchronous operations.
408  

408  

409  
        All outstanding operations complete with `errc::operation_canceled`.
409  
        All outstanding operations complete with `errc::operation_canceled`.
410  
        Check `ec == cond::canceled` for portable comparison.
410  
        Check `ec == cond::canceled` for portable comparison.
411  
    */
411  
    */
412  
    void cancel();
412  
    void cancel();
413  

413  

414  
public:
414  
public:
415  
    struct implementation : io_object::implementation
415  
    struct implementation : io_object::implementation
416  
    {
416  
    {
417  
        virtual std::coroutine_handle<> resolve(
417  
        virtual std::coroutine_handle<> resolve(
418  
            std::coroutine_handle<>,
418  
            std::coroutine_handle<>,
419  
            capy::executor_ref,
419  
            capy::executor_ref,
420  
            std::string_view host,
420  
            std::string_view host,
421  
            std::string_view service,
421  
            std::string_view service,
422  
            resolve_flags flags,
422  
            resolve_flags flags,
423  
            std::stop_token,
423  
            std::stop_token,
424  
            std::error_code*,
424  
            std::error_code*,
425  
            resolver_results*) = 0;
425  
            resolver_results*) = 0;
426  

426  

427  
        virtual std::coroutine_handle<> reverse_resolve(
427  
        virtual std::coroutine_handle<> reverse_resolve(
428  
            std::coroutine_handle<>,
428  
            std::coroutine_handle<>,
429  
            capy::executor_ref,
429  
            capy::executor_ref,
430  
            endpoint const& ep,
430  
            endpoint const& ep,
431  
            reverse_flags flags,
431  
            reverse_flags flags,
432  
            std::stop_token,
432  
            std::stop_token,
433  
            std::error_code*,
433  
            std::error_code*,
434  
            reverse_resolver_result*) = 0;
434  
            reverse_resolver_result*) = 0;
435  

435  

436  
        virtual void cancel() noexcept = 0;
436  
        virtual void cancel() noexcept = 0;
437  
    };
437  
    };
438  

438  

439  
protected:
439  
protected:
440  
    explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
440  
    explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
441  

441  

442  
private:
442  
private:
443  
    inline implementation& get() const noexcept
443  
    inline implementation& get() const noexcept
444  
    {
444  
    {
445  
        return *static_cast<implementation*>(h_.get());
445  
        return *static_cast<implementation*>(h_.get());
446  
    }
446  
    }
447  
};
447  
};
448  

448  

449  
} // namespace boost::corosio
449  
} // namespace boost::corosio
450  

450  

451  
#endif
451  
#endif