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. After the move,
301  
        Transfers ownership of the resolver resources. After the move,
302  
        @p other is in a moved-from state and may only be destroyed or
302  
        @p other is in a moved-from state and may only be destroyed or
303  
        assigned to.
303  
        assigned to.
304  

304  

305  
        @param other The resolver to move from.
305  
        @param other The resolver to move from.
306  

306  

307  
        @pre No awaitables returned by @p other's `resolve` methods
307  
        @pre No awaitables returned by @p other's `resolve` methods
308  
            exist.
308  
            exist.
309  
        @pre The execution context associated with @p other must
309  
        @pre The execution context associated with @p other must
310  
            outlive this resolver.
310  
            outlive this resolver.
311  
    */
311  
    */
312  
    resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
312  
    resolver(resolver&& other) noexcept : io_object(std::move(other)) {}
313  

313  

314  
    /** Move assignment operator.
314  
    /** Move assignment operator.
315  

315  

316  
        Destroys the current implementation and transfers ownership
316  
        Destroys the current implementation and transfers ownership
317  
        from @p other. After the move, @p other is in a moved-from
317  
        from @p other. After the move, @p other is in a moved-from
318  
        state and may only be destroyed or assigned to.
318  
        state and may only be destroyed or assigned to.
319  

319  

320  
        @param other The resolver to move from.
320  
        @param other The resolver to move from.
321  

321  

322  
        @pre No awaitables returned by either `*this` or @p other's
322  
        @pre No awaitables returned by either `*this` or @p other's
323  
            `resolve` methods exist.
323  
            `resolve` methods exist.
324  
        @pre The execution context associated with @p other must
324  
        @pre The execution context associated with @p other must
325  
            outlive this resolver.
325  
            outlive this resolver.
326  

326  

327  
        @return Reference to this resolver.
327  
        @return Reference to this resolver.
328  
    */
328  
    */
329  
    resolver& operator=(resolver&& other) noexcept
329  
    resolver& operator=(resolver&& other) noexcept
330  
    {
330  
    {
331  
        if (this != &other)
331  
        if (this != &other)
332  
            h_ = std::move(other.h_);
332  
            h_ = std::move(other.h_);
333  
        return *this;
333  
        return *this;
334  
    }
334  
    }
335  

335  

336  
    resolver(resolver const&)            = delete;
336  
    resolver(resolver const&)            = delete;
337  
    resolver& operator=(resolver const&) = delete;
337  
    resolver& operator=(resolver const&) = delete;
338  

338  

339  
    /** Initiate an asynchronous resolve operation.
339  
    /** Initiate an asynchronous resolve operation.
340  

340  

341  
        Resolves the host and service names into a list of endpoints.
341  
        Resolves the host and service names into a list of endpoints.
342  

342  

343  
        This resolver must outlive the returned awaitable.
343  
        This resolver must outlive the returned awaitable.
344  

344  

345  
        @param host A string identifying a location. May be a descriptive
345  
        @param host A string identifying a location. May be a descriptive
346  
            name or a numeric address string.
346  
            name or a numeric address string.
347  

347  

348  
        @param service A string identifying the requested service. This may
348  
        @param service A string identifying the requested service. This may
349  
            be a descriptive name or a numeric string corresponding to a
349  
            be a descriptive name or a numeric string corresponding to a
350  
            port number.
350  
            port number.
351  

351  

352  
        @return An awaitable that completes with `io_result<resolver_results>`.
352  
        @return An awaitable that completes with `io_result<resolver_results>`.
353  

353  

354  
        @par Example
354  
        @par Example
355  
        @code
355  
        @code
356  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
356  
        auto [ec, results] = co_await r.resolve("www.example.com", "https");
357  
        @endcode
357  
        @endcode
358  
    */
358  
    */
359  
    auto resolve(std::string_view host, std::string_view service)
359  
    auto resolve(std::string_view host, std::string_view service)
360  
    {
360  
    {
361  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
361  
        return resolve_awaitable(*this, host, service, resolve_flags::none);
362  
    }
362  
    }
363  

363  

364  
    /** Initiate an asynchronous resolve operation with flags.
364  
    /** Initiate an asynchronous resolve operation with flags.
365  

365  

366  
        Resolves the host and service names into a list of endpoints.
366  
        Resolves the host and service names into a list of endpoints.
367  

367  

368  
        This resolver must outlive the returned awaitable.
368  
        This resolver must outlive the returned awaitable.
369  

369  

370  
        @param host A string identifying a location.
370  
        @param host A string identifying a location.
371  

371  

372  
        @param service A string identifying the requested service.
372  
        @param service A string identifying the requested service.
373  

373  

374  
        @param flags Flags controlling resolution behavior.
374  
        @param flags Flags controlling resolution behavior.
375  

375  

376  
        @return An awaitable that completes with `io_result<resolver_results>`.
376  
        @return An awaitable that completes with `io_result<resolver_results>`.
377  
    */
377  
    */
378  
    auto resolve(
378  
    auto resolve(
379  
        std::string_view host, std::string_view service, resolve_flags flags)
379  
        std::string_view host, std::string_view service, resolve_flags flags)
380  
    {
380  
    {
381  
        return resolve_awaitable(*this, host, service, flags);
381  
        return resolve_awaitable(*this, host, service, flags);
382  
    }
382  
    }
383  

383  

384  
    /** Initiate an asynchronous reverse resolve operation.
384  
    /** Initiate an asynchronous reverse resolve operation.
385  

385  

386  
        Resolves an endpoint into a hostname and service name using
386  
        Resolves an endpoint into a hostname and service name using
387  
        reverse DNS lookup (PTR record query).
387  
        reverse DNS lookup (PTR record query).
388  

388  

389  
        This resolver must outlive the returned awaitable.
389  
        This resolver must outlive the returned awaitable.
390  

390  

391  
        @param ep The endpoint to resolve.
391  
        @param ep The endpoint to resolve.
392  

392  

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

395  

396  
        @par Example
396  
        @par Example
397  
        @code
397  
        @code
398  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
398  
        endpoint ep(ipv4_address({127, 0, 0, 1}), 80);
399  
        auto [ec, result] = co_await r.resolve(ep);
399  
        auto [ec, result] = co_await r.resolve(ep);
400  
        if (!ec)
400  
        if (!ec)
401  
            std::cout << result.host_name() << ":" << result.service_name();
401  
            std::cout << result.host_name() << ":" << result.service_name();
402  
        @endcode
402  
        @endcode
403  
    */
403  
    */
404  
    auto resolve(endpoint const& ep)
404  
    auto resolve(endpoint const& ep)
405  
    {
405  
    {
406  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
406  
        return reverse_resolve_awaitable(*this, ep, reverse_flags::none);
407  
    }
407  
    }
408  

408  

409  
    /** Initiate an asynchronous reverse resolve operation with flags.
409  
    /** Initiate an asynchronous reverse resolve operation with flags.
410  

410  

411  
        Resolves an endpoint into a hostname and service name using
411  
        Resolves an endpoint into a hostname and service name using
412  
        reverse DNS lookup (PTR record query).
412  
        reverse DNS lookup (PTR record query).
413  

413  

414  
        This resolver must outlive the returned awaitable.
414  
        This resolver must outlive the returned awaitable.
415  

415  

416  
        @param ep The endpoint to resolve.
416  
        @param ep The endpoint to resolve.
417  

417  

418  
        @param flags Flags controlling resolution behavior. See reverse_flags.
418  
        @param flags Flags controlling resolution behavior. See reverse_flags.
419  

419  

420  
        @return An awaitable that completes with
420  
        @return An awaitable that completes with
421  
            `io_result<reverse_resolver_result>`.
421  
            `io_result<reverse_resolver_result>`.
422  
    */
422  
    */
423  
    auto resolve(endpoint const& ep, reverse_flags flags)
423  
    auto resolve(endpoint const& ep, reverse_flags flags)
424  
    {
424  
    {
425  
        return reverse_resolve_awaitable(*this, ep, flags);
425  
        return reverse_resolve_awaitable(*this, ep, flags);
426  
    }
426  
    }
427  

427  

428  
    /** Cancel any pending asynchronous operations.
428  
    /** Cancel any pending asynchronous operations.
429  

429  

430  
        All outstanding operations complete with `errc::operation_canceled`.
430  
        All outstanding operations complete with `errc::operation_canceled`.
431  
        Check `ec == cond::canceled` for portable comparison.
431  
        Check `ec == cond::canceled` for portable comparison.
432  
    */
432  
    */
433  
    void cancel();
433  
    void cancel();
434  

434  

435  
public:
435  
public:
436  
    struct implementation : io_object::implementation
436  
    struct implementation : io_object::implementation
437  
    {
437  
    {
438  
        virtual std::coroutine_handle<> resolve(
438  
        virtual std::coroutine_handle<> resolve(
439  
            std::coroutine_handle<>,
439  
            std::coroutine_handle<>,
440  
            capy::executor_ref,
440  
            capy::executor_ref,
441  
            std::string_view host,
441  
            std::string_view host,
442  
            std::string_view service,
442  
            std::string_view service,
443  
            resolve_flags flags,
443  
            resolve_flags flags,
444  
            std::stop_token,
444  
            std::stop_token,
445  
            std::error_code*,
445  
            std::error_code*,
446  
            resolver_results*) = 0;
446  
            resolver_results*) = 0;
447  

447  

448  
        virtual std::coroutine_handle<> reverse_resolve(
448  
        virtual std::coroutine_handle<> reverse_resolve(
449  
            std::coroutine_handle<>,
449  
            std::coroutine_handle<>,
450  
            capy::executor_ref,
450  
            capy::executor_ref,
451  
            endpoint const& ep,
451  
            endpoint const& ep,
452  
            reverse_flags flags,
452  
            reverse_flags flags,
453  
            std::stop_token,
453  
            std::stop_token,
454  
            std::error_code*,
454  
            std::error_code*,
455  
            reverse_resolver_result*) = 0;
455  
            reverse_resolver_result*) = 0;
456  

456  

457  
        virtual void cancel() noexcept = 0;
457  
        virtual void cancel() noexcept = 0;
458  
    };
458  
    };
459  

459  

460  
protected:
460  
protected:
461  
    explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
461  
    explicit resolver(handle h) noexcept : io_object(std::move(h)) {}
462  

462  

463  
private:
463  
private:
464  
    inline implementation& get() const noexcept
464  
    inline implementation& get() const noexcept
465  
    {
465  
    {
466  
        return *static_cast<implementation*>(h_.get());
466  
        return *static_cast<implementation*>(h_.get());
467  
    }
467  
    }
468  
};
468  
};
469  

469  

470  
} // namespace boost::corosio
470  
} // namespace boost::corosio
471  

471  

472  
#endif
472  
#endif