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  
//
4  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// 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)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
7  
//
7  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
8  
//
9  
//
9  

10  

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

13  

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

23  

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

25  

25  
#include <cassert>
26  
#include <cassert>
26  
#include <concepts>
27  
#include <concepts>
27 -
#include <cstdint>
 
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 -

 
102  

101  

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

103  

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

110  

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

113  

115  
    /// 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.
116  
    numeric_service = 0x02,
115  
    numeric_service = 0x02,
117  

116  

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

119  

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

123  

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

131  

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

139  

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

147  

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

 
157  

155  

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

157  

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

161  

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

166  

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

171  

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

176  

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

181  

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

184  

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

200  

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

212  

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

217  

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

224  

227  
        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)
228  
            -> std::coroutine_handle<>
226  
            -> std::coroutine_handle<>
229  
        {
227  
        {
230  
            token_ = env->stop_token;
228  
            token_ = env->stop_token;
231  
            return r_.get().resolve(
229  
            return r_.get().resolve(
232  
                h, env->executor, host_, service_, flags_, token_, &ec_,
230  
                h, env->executor, host_, service_, flags_, token_, &ec_,
233  
                &results_);
231  
                &results_);
234  
        }
232  
        }
235  
    };
233  
    };
236  

234  

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

243  

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

251  

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

256  

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

263  

266  
        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)
267  
            -> std::coroutine_handle<>
265  
            -> std::coroutine_handle<>
268  
        {
266  
        {
269  
            token_ = env->stop_token;
267  
            token_ = env->stop_token;
270  
            return r_.get().reverse_resolve(
268  
            return r_.get().reverse_resolve(
271  
                h, env->executor, ep_, flags_, token_, &ec_, &result_);
269  
                h, env->executor, ep_, flags_, token_, &ec_, &result_);
272  
        }
270  
        }
273  
    };
271  
    };
274  

272  

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

275  

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

279  

282  
    /** Construct a resolver from an execution context.
280  
    /** Construct a resolver from an execution context.
283  

281  

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

285  

288  
    /** Construct a resolver from an executor.
286  
    /** Construct a resolver from an executor.
289  

287  

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

289  

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

298  

301  
    /** Move constructor.
299  
    /** Move constructor.
302  

300  

303  
        Transfers ownership of the resolver resources.
301  
        Transfers ownership of the resolver resources.
304  

302  

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

306  

309  
    /** Move assignment operator.
307  
    /** Move assignment operator.
310  

308  

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

311  

314  
        @param other The resolver to move from.
312  
        @param other The resolver to move from.
315  

313  

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

322  

325 -
    resolver(resolver const&) = delete;
323 +
    resolver(resolver const&)            = delete;
326  
    resolver& operator=(resolver const&) = delete;
324  
    resolver& operator=(resolver const&) = delete;
327  

325  

328  
    /** Initiate an asynchronous resolve operation.
326  
    /** Initiate an asynchronous resolve operation.
329  

327  

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

329  

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

332  

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

336  

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

338  

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

348  

351  
    /** Initiate an asynchronous resolve operation with flags.
349  
    /** Initiate an asynchronous resolve operation with flags.
352  

350  

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

352  

355  
        @param host A string identifying a location.
353  
        @param host A string identifying a location.
356  

354  

357  
        @param service A string identifying the requested service.
355  
        @param service A string identifying the requested service.
358  

356  

359  
        @param flags Flags controlling resolution behavior.
357  
        @param flags Flags controlling resolution behavior.
360  

358  

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

366  

369  
    /** Initiate an asynchronous reverse resolve operation.
367  
    /** Initiate an asynchronous reverse resolve operation.
370  

368  

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

371  

374  
        @param ep The endpoint to resolve.
372  
        @param ep The endpoint to resolve.
375  

373  

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

376  

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

389  

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

391  

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

394  

397  
        @param ep The endpoint to resolve.
395  
        @param ep The endpoint to resolve.
398  

396  

399  
        @param flags Flags controlling resolution behavior. See reverse_flags.
397  
        @param flags Flags controlling resolution behavior. See reverse_flags.
400  

398  

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

406  

409  
    /** Cancel any pending asynchronous operations.
407  
    /** Cancel any pending asynchronous operations.
410  

408  

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

413  

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

426  

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

435  

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

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

441  

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

448  

448  
} // namespace boost::corosio
449  
} // namespace boost::corosio
449  

450  

450  
#endif
451  
#endif