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

9  

10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
12  

12  

13  
#include <boost/corosio/detail/platform.hpp>
13  
#include <boost/corosio/detail/platform.hpp>
14  

14  

15  
#if BOOST_COROSIO_POSIX
15  
#if BOOST_COROSIO_POSIX
16  

16  

17  
#include <boost/corosio/native/detail/posix/posix_resolver.hpp>
17  
#include <boost/corosio/native/detail/posix/posix_resolver.hpp>
18  
#include <boost/corosio/detail/thread_pool.hpp>
18  
#include <boost/corosio/detail/thread_pool.hpp>
19  

19  

20  
#include <unordered_map>
20  
#include <unordered_map>
21  

21  

22  
namespace boost::corosio::detail {
22  
namespace boost::corosio::detail {
23  

23  

24  
/** Resolver service for POSIX backends.
24  
/** Resolver service for POSIX backends.
25  

25  

26  
    Owns all posix_resolver instances. Thread lifecycle is managed
26  
    Owns all posix_resolver instances. Thread lifecycle is managed
27  
    by the thread_pool service.
27  
    by the thread_pool service.
28  
*/
28  
*/
29  
class BOOST_COROSIO_DECL posix_resolver_service final
29  
class BOOST_COROSIO_DECL posix_resolver_service final
30  
    : public capy::execution_context::service
30  
    : public capy::execution_context::service
31  
    , public io_object::io_service
31  
    , public io_object::io_service
32  
{
32  
{
33  
public:
33  
public:
34  
    using key_type = posix_resolver_service;
34  
    using key_type = posix_resolver_service;
35  

35  

36  
    posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
36  
    posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
37  
        : sched_(&sched)
37  
        : sched_(&sched)
38  
        , pool_(ctx.make_service<thread_pool>())
38  
        , pool_(ctx.make_service<thread_pool>())
39  
    {
39  
    {
40  
    }
40  
    }
41  

41  

42  
    ~posix_resolver_service() override = default;
42  
    ~posix_resolver_service() override = default;
43  

43  

44  
    posix_resolver_service(posix_resolver_service const&)            = delete;
44  
    posix_resolver_service(posix_resolver_service const&)            = delete;
45  
    posix_resolver_service& operator=(posix_resolver_service const&) = delete;
45  
    posix_resolver_service& operator=(posix_resolver_service const&) = delete;
46  

46  

47  
    io_object::implementation* construct() override;
47  
    io_object::implementation* construct() override;
48  

48  

49  
    void destroy(io_object::implementation* p) override
49  
    void destroy(io_object::implementation* p) override
50  
    {
50  
    {
51  
        auto& impl = static_cast<posix_resolver&>(*p);
51  
        auto& impl = static_cast<posix_resolver&>(*p);
52  
        impl.cancel();
52  
        impl.cancel();
53  
        destroy_impl(impl);
53  
        destroy_impl(impl);
54  
    }
54  
    }
55  

55  

56  
    void shutdown() override;
56  
    void shutdown() override;
57  
    void destroy_impl(posix_resolver& impl);
57  
    void destroy_impl(posix_resolver& impl);
58  

58  

59  
    void post(scheduler_op* op);
59  
    void post(scheduler_op* op);
60  
    void work_started() noexcept;
60  
    void work_started() noexcept;
61  
    void work_finished() noexcept;
61  
    void work_finished() noexcept;
62  

62  

63  
    /** Return the resolver thread pool. */
63  
    /** Return the resolver thread pool. */
64  
    thread_pool& pool() noexcept
64  
    thread_pool& pool() noexcept
65  
    {
65  
    {
66  
        return pool_;
66  
        return pool_;
67  
    }
67  
    }
68  

68  

69  
private:
69  
private:
70  
    scheduler* sched_;
70  
    scheduler* sched_;
71  
    thread_pool& pool_;
71  
    thread_pool& pool_;
72  
    std::mutex mutex_;
72  
    std::mutex mutex_;
73  
    intrusive_list<posix_resolver> resolver_list_;
73  
    intrusive_list<posix_resolver> resolver_list_;
74  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
74  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
75  
        resolver_ptrs_;
75  
        resolver_ptrs_;
76  
};
76  
};
77  

77  

78  
/** Get or create the resolver service for the given context.
78  
/** Get or create the resolver service for the given context.
79  

79  

80  
    This function is called by the concrete scheduler during initialization
80  
    This function is called by the concrete scheduler during initialization
81  
    to create the resolver service with a reference to itself.
81  
    to create the resolver service with a reference to itself.
82  

82  

83  
    @param ctx Reference to the owning execution_context.
83  
    @param ctx Reference to the owning execution_context.
84  
    @param sched Reference to the scheduler for posting completions.
84  
    @param sched Reference to the scheduler for posting completions.
85  
    @return Reference to the resolver service.
85  
    @return Reference to the resolver service.
86  
*/
86  
*/
87  
posix_resolver_service&
87  
posix_resolver_service&
88  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
88  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
89  

89  

90  
// ---------------------------------------------------------------------------
90  
// ---------------------------------------------------------------------------
91  
// Inline implementation
91  
// Inline implementation
92  
// ---------------------------------------------------------------------------
92  
// ---------------------------------------------------------------------------
93  

93  

94  
// posix_resolver_detail helpers
94  
// posix_resolver_detail helpers
95  

95  

96  
inline int
96  
inline int
97  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
97  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
98  
{
98  
{
99  
    int hints = 0;
99  
    int hints = 0;
100  

100  

101  
    if ((flags & resolve_flags::passive) != resolve_flags::none)
101  
    if ((flags & resolve_flags::passive) != resolve_flags::none)
102  
        hints |= AI_PASSIVE;
102  
        hints |= AI_PASSIVE;
103  
    if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
103  
    if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
104  
        hints |= AI_NUMERICHOST;
104  
        hints |= AI_NUMERICHOST;
105  
    if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
105  
    if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
106  
        hints |= AI_NUMERICSERV;
106  
        hints |= AI_NUMERICSERV;
107  
    if ((flags & resolve_flags::address_configured) != resolve_flags::none)
107  
    if ((flags & resolve_flags::address_configured) != resolve_flags::none)
108  
        hints |= AI_ADDRCONFIG;
108  
        hints |= AI_ADDRCONFIG;
109  
    if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
109  
    if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
110  
        hints |= AI_V4MAPPED;
110  
        hints |= AI_V4MAPPED;
111  
    if ((flags & resolve_flags::all_matching) != resolve_flags::none)
111  
    if ((flags & resolve_flags::all_matching) != resolve_flags::none)
112  
        hints |= AI_ALL;
112  
        hints |= AI_ALL;
113  

113  

114  
    return hints;
114  
    return hints;
115  
}
115  
}
116  

116  

117  
inline int
117  
inline int
118  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
118  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
119  
{
119  
{
120  
    int ni_flags = 0;
120  
    int ni_flags = 0;
121  

121  

122  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
122  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
123  
        ni_flags |= NI_NUMERICHOST;
123  
        ni_flags |= NI_NUMERICHOST;
124  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
124  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
125  
        ni_flags |= NI_NUMERICSERV;
125  
        ni_flags |= NI_NUMERICSERV;
126  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
126  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
127  
        ni_flags |= NI_NAMEREQD;
127  
        ni_flags |= NI_NAMEREQD;
128  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
128  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
129  
        ni_flags |= NI_DGRAM;
129  
        ni_flags |= NI_DGRAM;
130  

130  

131  
    return ni_flags;
131  
    return ni_flags;
132  
}
132  
}
133  

133  

134  
inline resolver_results
134  
inline resolver_results
135  
posix_resolver_detail::convert_results(
135  
posix_resolver_detail::convert_results(
136  
    struct addrinfo* ai, std::string_view host, std::string_view service)
136  
    struct addrinfo* ai, std::string_view host, std::string_view service)
137  
{
137  
{
138  
    std::vector<resolver_entry> entries;
138  
    std::vector<resolver_entry> entries;
139  
    entries.reserve(4); // Most lookups return 1-4 addresses
139  
    entries.reserve(4); // Most lookups return 1-4 addresses
140  

140  

141  
    for (auto* p = ai; p != nullptr; p = p->ai_next)
141  
    for (auto* p = ai; p != nullptr; p = p->ai_next)
142  
    {
142  
    {
143  
        if (p->ai_family == AF_INET)
143  
        if (p->ai_family == AF_INET)
144  
        {
144  
        {
145  
            auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
145  
            auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
146  
            auto ep    = from_sockaddr_in(*addr);
146  
            auto ep    = from_sockaddr_in(*addr);
147  
            entries.emplace_back(ep, host, service);
147  
            entries.emplace_back(ep, host, service);
148  
        }
148  
        }
149  
        else if (p->ai_family == AF_INET6)
149  
        else if (p->ai_family == AF_INET6)
150  
        {
150  
        {
151  
            auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
151  
            auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
152  
            auto ep    = from_sockaddr_in6(*addr);
152  
            auto ep    = from_sockaddr_in6(*addr);
153  
            entries.emplace_back(ep, host, service);
153  
            entries.emplace_back(ep, host, service);
154  
        }
154  
        }
155  
    }
155  
    }
156  

156  

157  
    return resolver_results(std::move(entries));
157  
    return resolver_results(std::move(entries));
158  
}
158  
}
159  

159  

160  
inline std::error_code
160  
inline std::error_code
161  
posix_resolver_detail::make_gai_error(int gai_err)
161  
posix_resolver_detail::make_gai_error(int gai_err)
162  
{
162  
{
163  
    // Map GAI errors to appropriate generic error codes
163  
    // Map GAI errors to appropriate generic error codes
164  
    switch (gai_err)
164  
    switch (gai_err)
165  
    {
165  
    {
166  
    case EAI_AGAIN:
166  
    case EAI_AGAIN:
167  
        // Temporary failure - try again later
167  
        // Temporary failure - try again later
168  
        return std::error_code(
168  
        return std::error_code(
169  
            static_cast<int>(std::errc::resource_unavailable_try_again),
169  
            static_cast<int>(std::errc::resource_unavailable_try_again),
170  
            std::generic_category());
170  
            std::generic_category());
171  

171  

172  
    case EAI_BADFLAGS:
172  
    case EAI_BADFLAGS:
173  
        // Invalid flags
173  
        // Invalid flags
174  
        return std::error_code(
174  
        return std::error_code(
175  
            static_cast<int>(std::errc::invalid_argument),
175  
            static_cast<int>(std::errc::invalid_argument),
176  
            std::generic_category());
176  
            std::generic_category());
177  

177  

178  
    case EAI_FAIL:
178  
    case EAI_FAIL:
179  
        // Non-recoverable failure
179  
        // Non-recoverable failure
180  
        return std::error_code(
180  
        return std::error_code(
181  
            static_cast<int>(std::errc::io_error), std::generic_category());
181  
            static_cast<int>(std::errc::io_error), std::generic_category());
182  

182  

183  
    case EAI_FAMILY:
183  
    case EAI_FAMILY:
184  
        // Address family not supported
184  
        // Address family not supported
185  
        return std::error_code(
185  
        return std::error_code(
186  
            static_cast<int>(std::errc::address_family_not_supported),
186  
            static_cast<int>(std::errc::address_family_not_supported),
187  
            std::generic_category());
187  
            std::generic_category());
188  

188  

189  
    case EAI_MEMORY:
189  
    case EAI_MEMORY:
190  
        // Memory allocation failure
190  
        // Memory allocation failure
191  
        return std::error_code(
191  
        return std::error_code(
192  
            static_cast<int>(std::errc::not_enough_memory),
192  
            static_cast<int>(std::errc::not_enough_memory),
193  
            std::generic_category());
193  
            std::generic_category());
194  

194  

195  
    case EAI_NONAME:
195  
    case EAI_NONAME:
196  
        // Host or service not found
196  
        // Host or service not found
197  
        return std::error_code(
197  
        return std::error_code(
198  
            static_cast<int>(std::errc::no_such_device_or_address),
198  
            static_cast<int>(std::errc::no_such_device_or_address),
199  
            std::generic_category());
199  
            std::generic_category());
200  

200  

201  
    case EAI_SERVICE:
201  
    case EAI_SERVICE:
202  
        // Service not supported for socket type
202  
        // Service not supported for socket type
203  
        return std::error_code(
203  
        return std::error_code(
204  
            static_cast<int>(std::errc::invalid_argument),
204  
            static_cast<int>(std::errc::invalid_argument),
205  
            std::generic_category());
205  
            std::generic_category());
206  

206  

207  
    case EAI_SOCKTYPE:
207  
    case EAI_SOCKTYPE:
208  
        // Socket type not supported
208  
        // Socket type not supported
209  
        return std::error_code(
209  
        return std::error_code(
210  
            static_cast<int>(std::errc::not_supported),
210  
            static_cast<int>(std::errc::not_supported),
211  
            std::generic_category());
211  
            std::generic_category());
212  

212  

213  
    case EAI_SYSTEM:
213  
    case EAI_SYSTEM:
214  
        // System error - use errno
214  
        // System error - use errno
215  
        return std::error_code(errno, std::generic_category());
215  
        return std::error_code(errno, std::generic_category());
216  

216  

217  
    default:
217  
    default:
218  
        // Unknown error
218  
        // Unknown error
219  
        return std::error_code(
219  
        return std::error_code(
220  
            static_cast<int>(std::errc::io_error), std::generic_category());
220  
            static_cast<int>(std::errc::io_error), std::generic_category());
221  
    }
221  
    }
222  
}
222  
}
223  

223  

224  
// posix_resolver
224  
// posix_resolver
225  

225  

226  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
226  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
227  
    : svc_(svc)
227  
    : svc_(svc)
228  
{
228  
{
229  
}
229  
}
230  

230  

231  
// posix_resolver::resolve_op implementation
231  
// posix_resolver::resolve_op implementation
232  

232  

233  
inline void
233  
inline void
234  
posix_resolver::resolve_op::reset() noexcept
234  
posix_resolver::resolve_op::reset() noexcept
235  
{
235  
{
236  
    host.clear();
236  
    host.clear();
237  
    service.clear();
237  
    service.clear();
238  
    flags          = resolve_flags::none;
238  
    flags          = resolve_flags::none;
239  
    stored_results = resolver_results{};
239  
    stored_results = resolver_results{};
240  
    gai_error      = 0;
240  
    gai_error      = 0;
241  
    cancelled.store(false, std::memory_order_relaxed);
241  
    cancelled.store(false, std::memory_order_relaxed);
242  
    stop_cb.reset();
242  
    stop_cb.reset();
243  
    ec_out = nullptr;
243  
    ec_out = nullptr;
244  
    out    = nullptr;
244  
    out    = nullptr;
245  
}
245  
}
246  

246  

247  
inline void
247  
inline void
248  
posix_resolver::resolve_op::operator()()
248  
posix_resolver::resolve_op::operator()()
249  
{
249  
{
250  
    stop_cb.reset(); // Disconnect stop callback
250  
    stop_cb.reset(); // Disconnect stop callback
251  

251  

252  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
252  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
253  

253  

254  
    if (ec_out)
254  
    if (ec_out)
255  
    {
255  
    {
256  
        if (was_cancelled)
256  
        if (was_cancelled)
257  
            *ec_out = capy::error::canceled;
257  
            *ec_out = capy::error::canceled;
258  
        else if (gai_error != 0)
258  
        else if (gai_error != 0)
259  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
259  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
260  
        else
260  
        else
261  
            *ec_out = {}; // Clear on success
261  
            *ec_out = {}; // Clear on success
262  
    }
262  
    }
263  

263  

264  
    if (out && !was_cancelled && gai_error == 0)
264  
    if (out && !was_cancelled && gai_error == 0)
265  
        *out = std::move(stored_results);
265  
        *out = std::move(stored_results);
266  

266  

267  
    impl->svc_.work_finished();
267  
    impl->svc_.work_finished();
268  
    cont_op.cont.h = h;
268  
    cont_op.cont.h = h;
269  
    dispatch_coro(ex, cont_op.cont).resume();
269  
    dispatch_coro(ex, cont_op.cont).resume();
270  
}
270  
}
271  

271  

272  
inline void
272  
inline void
273  
posix_resolver::resolve_op::destroy()
273  
posix_resolver::resolve_op::destroy()
274  
{
274  
{
275  
    stop_cb.reset();
275  
    stop_cb.reset();
276  
}
276  
}
277  

277  

278  
inline void
278  
inline void
279  
posix_resolver::resolve_op::request_cancel() noexcept
279  
posix_resolver::resolve_op::request_cancel() noexcept
280  
{
280  
{
281  
    cancelled.store(true, std::memory_order_release);
281  
    cancelled.store(true, std::memory_order_release);
282  
}
282  
}
283  

283  

284  
inline void
284  
inline void
285  
posix_resolver::resolve_op::start(std::stop_token const& token)
285  
posix_resolver::resolve_op::start(std::stop_token const& token)
286  
{
286  
{
287  
    cancelled.store(false, std::memory_order_release);
287  
    cancelled.store(false, std::memory_order_release);
288  
    stop_cb.reset();
288  
    stop_cb.reset();
289  

289  

290  
    if (token.stop_possible())
290  
    if (token.stop_possible())
291  
        stop_cb.emplace(token, canceller{this});
291  
        stop_cb.emplace(token, canceller{this});
292  
}
292  
}
293  

293  

294  
// posix_resolver::reverse_resolve_op implementation
294  
// posix_resolver::reverse_resolve_op implementation
295  

295  

296  
inline void
296  
inline void
297  
posix_resolver::reverse_resolve_op::reset() noexcept
297  
posix_resolver::reverse_resolve_op::reset() noexcept
298  
{
298  
{
299  
    ep    = endpoint{};
299  
    ep    = endpoint{};
300  
    flags = reverse_flags::none;
300  
    flags = reverse_flags::none;
301  
    stored_host.clear();
301  
    stored_host.clear();
302  
    stored_service.clear();
302  
    stored_service.clear();
303  
    gai_error = 0;
303  
    gai_error = 0;
304  
    cancelled.store(false, std::memory_order_relaxed);
304  
    cancelled.store(false, std::memory_order_relaxed);
305  
    stop_cb.reset();
305  
    stop_cb.reset();
306  
    ec_out     = nullptr;
306  
    ec_out     = nullptr;
307  
    result_out = nullptr;
307  
    result_out = nullptr;
308  
}
308  
}
309  

309  

310  
inline void
310  
inline void
311  
posix_resolver::reverse_resolve_op::operator()()
311  
posix_resolver::reverse_resolve_op::operator()()
312  
{
312  
{
313  
    stop_cb.reset(); // Disconnect stop callback
313  
    stop_cb.reset(); // Disconnect stop callback
314  

314  

315  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
315  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
316  

316  

317  
    if (ec_out)
317  
    if (ec_out)
318  
    {
318  
    {
319  
        if (was_cancelled)
319  
        if (was_cancelled)
320  
            *ec_out = capy::error::canceled;
320  
            *ec_out = capy::error::canceled;
321  
        else if (gai_error != 0)
321  
        else if (gai_error != 0)
322  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
322  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
323  
        else
323  
        else
324  
            *ec_out = {}; // Clear on success
324  
            *ec_out = {}; // Clear on success
325  
    }
325  
    }
326  

326  

327  
    if (result_out && !was_cancelled && gai_error == 0)
327  
    if (result_out && !was_cancelled && gai_error == 0)
328  
    {
328  
    {
329  
        *result_out = reverse_resolver_result(
329  
        *result_out = reverse_resolver_result(
330  
            ep, std::move(stored_host), std::move(stored_service));
330  
            ep, std::move(stored_host), std::move(stored_service));
331  
    }
331  
    }
332  

332  

333  
    impl->svc_.work_finished();
333  
    impl->svc_.work_finished();
334  
    cont_op.cont.h = h;
334  
    cont_op.cont.h = h;
335  
    dispatch_coro(ex, cont_op.cont).resume();
335  
    dispatch_coro(ex, cont_op.cont).resume();
336  
}
336  
}
337  

337  

338  
inline void
338  
inline void
339  
posix_resolver::reverse_resolve_op::destroy()
339  
posix_resolver::reverse_resolve_op::destroy()
340  
{
340  
{
341  
    stop_cb.reset();
341  
    stop_cb.reset();
342  
}
342  
}
343  

343  

344  
inline void
344  
inline void
345  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
345  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
346  
{
346  
{
347  
    cancelled.store(true, std::memory_order_release);
347  
    cancelled.store(true, std::memory_order_release);
348  
}
348  
}
349  

349  

350  
inline void
350  
inline void
351  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
351  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
352  
{
352  
{
353  
    cancelled.store(false, std::memory_order_release);
353  
    cancelled.store(false, std::memory_order_release);
354  
    stop_cb.reset();
354  
    stop_cb.reset();
355  

355  

356  
    if (token.stop_possible())
356  
    if (token.stop_possible())
357  
        stop_cb.emplace(token, canceller{this});
357  
        stop_cb.emplace(token, canceller{this});
358  
}
358  
}
359  

359  

360  
// posix_resolver implementation
360  
// posix_resolver implementation
361  

361  

362  
inline std::coroutine_handle<>
362  
inline std::coroutine_handle<>
363  
posix_resolver::resolve(
363  
posix_resolver::resolve(
364  
    std::coroutine_handle<> h,
364  
    std::coroutine_handle<> h,
365  
    capy::executor_ref ex,
365  
    capy::executor_ref ex,
366  
    std::string_view host,
366  
    std::string_view host,
367  
    std::string_view service,
367  
    std::string_view service,
368  
    resolve_flags flags,
368  
    resolve_flags flags,
369  
    std::stop_token token,
369  
    std::stop_token token,
370  
    std::error_code* ec,
370  
    std::error_code* ec,
371  
    resolver_results* out)
371  
    resolver_results* out)
372  
{
372  
{
373  
    auto& op = op_;
373  
    auto& op = op_;
374  
    op.reset();
374  
    op.reset();
375  
    op.h       = h;
375  
    op.h       = h;
376  
    op.ex      = ex;
376  
    op.ex      = ex;
377  
    op.impl    = this;
377  
    op.impl    = this;
378  
    op.ec_out  = ec;
378  
    op.ec_out  = ec;
379  
    op.out     = out;
379  
    op.out     = out;
380  
    op.host    = host;
380  
    op.host    = host;
381  
    op.service = service;
381  
    op.service = service;
382  
    op.flags   = flags;
382  
    op.flags   = flags;
383  
    op.start(token);
383  
    op.start(token);
384  

384  

385  
    // Keep io_context alive while resolution is pending
385  
    // Keep io_context alive while resolution is pending
386  
    op.ex.on_work_started();
386  
    op.ex.on_work_started();
387  

387  

388  
    // Prevent impl destruction while work is in flight
388  
    // Prevent impl destruction while work is in flight
389  
    resolve_pool_op_.resolver_ = this;
389  
    resolve_pool_op_.resolver_ = this;
390  
    resolve_pool_op_.ref_      = this->shared_from_this();
390  
    resolve_pool_op_.ref_      = this->shared_from_this();
391  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
391  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
392  
    if (!svc_.pool().post(&resolve_pool_op_))
392  
    if (!svc_.pool().post(&resolve_pool_op_))
393  
    {
393  
    {
394  
        // Pool shut down — complete with cancellation
394  
        // Pool shut down — complete with cancellation
395  
        resolve_pool_op_.ref_.reset();
395  
        resolve_pool_op_.ref_.reset();
396  
        op.cancelled.store(true, std::memory_order_release);
396  
        op.cancelled.store(true, std::memory_order_release);
397  
        svc_.post(&op_);
397  
        svc_.post(&op_);
398  
    }
398  
    }
399  
    return std::noop_coroutine();
399  
    return std::noop_coroutine();
400  
}
400  
}
401  

401  

402  
inline std::coroutine_handle<>
402  
inline std::coroutine_handle<>
403  
posix_resolver::reverse_resolve(
403  
posix_resolver::reverse_resolve(
404  
    std::coroutine_handle<> h,
404  
    std::coroutine_handle<> h,
405  
    capy::executor_ref ex,
405  
    capy::executor_ref ex,
406  
    endpoint const& ep,
406  
    endpoint const& ep,
407  
    reverse_flags flags,
407  
    reverse_flags flags,
408  
    std::stop_token token,
408  
    std::stop_token token,
409  
    std::error_code* ec,
409  
    std::error_code* ec,
410  
    reverse_resolver_result* result_out)
410  
    reverse_resolver_result* result_out)
411  
{
411  
{
412  
    auto& op = reverse_op_;
412  
    auto& op = reverse_op_;
413  
    op.reset();
413  
    op.reset();
414  
    op.h          = h;
414  
    op.h          = h;
415  
    op.ex         = ex;
415  
    op.ex         = ex;
416  
    op.impl       = this;
416  
    op.impl       = this;
417  
    op.ec_out     = ec;
417  
    op.ec_out     = ec;
418  
    op.result_out = result_out;
418  
    op.result_out = result_out;
419  
    op.ep         = ep;
419  
    op.ep         = ep;
420  
    op.flags      = flags;
420  
    op.flags      = flags;
421  
    op.start(token);
421  
    op.start(token);
422  

422  

423  
    // Keep io_context alive while resolution is pending
423  
    // Keep io_context alive while resolution is pending
424  
    op.ex.on_work_started();
424  
    op.ex.on_work_started();
425  

425  

426  
    // Prevent impl destruction while work is in flight
426  
    // Prevent impl destruction while work is in flight
427  
    reverse_pool_op_.resolver_ = this;
427  
    reverse_pool_op_.resolver_ = this;
428  
    reverse_pool_op_.ref_      = this->shared_from_this();
428  
    reverse_pool_op_.ref_      = this->shared_from_this();
429  
    reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
429  
    reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
430  
    if (!svc_.pool().post(&reverse_pool_op_))
430  
    if (!svc_.pool().post(&reverse_pool_op_))
431  
    {
431  
    {
432  
        // Pool shut down — complete with cancellation
432  
        // Pool shut down — complete with cancellation
433  
        reverse_pool_op_.ref_.reset();
433  
        reverse_pool_op_.ref_.reset();
434  
        op.cancelled.store(true, std::memory_order_release);
434  
        op.cancelled.store(true, std::memory_order_release);
435  
        svc_.post(&reverse_op_);
435  
        svc_.post(&reverse_op_);
436  
    }
436  
    }
437  
    return std::noop_coroutine();
437  
    return std::noop_coroutine();
438  
}
438  
}
439  

439  

440  
inline void
440  
inline void
441  
posix_resolver::cancel() noexcept
441  
posix_resolver::cancel() noexcept
442  
{
442  
{
443  
    op_.request_cancel();
443  
    op_.request_cancel();
444  
    reverse_op_.request_cancel();
444  
    reverse_op_.request_cancel();
445  
}
445  
}
446  

446  

447  
inline void
447  
inline void
448  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
448  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
449  
{
449  
{
450  
    auto* pw   = static_cast<pool_op*>(w);
450  
    auto* pw   = static_cast<pool_op*>(w);
451  
    auto* self = pw->resolver_;
451  
    auto* self = pw->resolver_;
452  

452  

453  
    struct addrinfo hints{};
453  
    struct addrinfo hints{};
454  
    hints.ai_family   = AF_UNSPEC;
454  
    hints.ai_family   = AF_UNSPEC;
455  
    hints.ai_socktype = SOCK_STREAM;
455  
    hints.ai_socktype = SOCK_STREAM;
456  
    hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
456  
    hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
457  

457  

458  
    struct addrinfo* ai = nullptr;
458  
    struct addrinfo* ai = nullptr;
459  
    int result          = ::getaddrinfo(
459  
    int result          = ::getaddrinfo(
460  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
460  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
461  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
461  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
462  
        &ai);
462  
        &ai);
463  

463  

464  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
464  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
465  
    {
465  
    {
466  
        if (result == 0 && ai)
466  
        if (result == 0 && ai)
467  
        {
467  
        {
468  
            self->op_.stored_results = posix_resolver_detail::convert_results(
468  
            self->op_.stored_results = posix_resolver_detail::convert_results(
469  
                ai, self->op_.host, self->op_.service);
469  
                ai, self->op_.host, self->op_.service);
470  
            self->op_.gai_error = 0;
470  
            self->op_.gai_error = 0;
471  
        }
471  
        }
472  
        else
472  
        else
473  
        {
473  
        {
474  
            self->op_.gai_error = result;
474  
            self->op_.gai_error = result;
475  
        }
475  
        }
476  
    }
476  
    }
477  

477  

478  
    if (ai)
478  
    if (ai)
479  
        ::freeaddrinfo(ai);
479  
        ::freeaddrinfo(ai);
480  

480  

481  
    // Move ref to stack before post — post may trigger destroy_impl
481  
    // Move ref to stack before post — post may trigger destroy_impl
482  
    // which erases the last shared_ptr, destroying *self (and *pw)
482  
    // which erases the last shared_ptr, destroying *self (and *pw)
483  
    auto ref = std::move(pw->ref_);
483  
    auto ref = std::move(pw->ref_);
484  
    self->svc_.post(&self->op_);
484  
    self->svc_.post(&self->op_);
485  
}
485  
}
486  

486  

487  
inline void
487  
inline void
488  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
488  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
489  
{
489  
{
490  
    auto* pw   = static_cast<pool_op*>(w);
490  
    auto* pw   = static_cast<pool_op*>(w);
491  
    auto* self = pw->resolver_;
491  
    auto* self = pw->resolver_;
492  

492  

493  
    sockaddr_storage ss{};
493  
    sockaddr_storage ss{};
494  
    socklen_t ss_len;
494  
    socklen_t ss_len;
495  

495  

496  
    if (self->reverse_op_.ep.is_v4())
496  
    if (self->reverse_op_.ep.is_v4())
497  
    {
497  
    {
498  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
498  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
499  
        std::memcpy(&ss, &sa, sizeof(sa));
499  
        std::memcpy(&ss, &sa, sizeof(sa));
500  
        ss_len = sizeof(sockaddr_in);
500  
        ss_len = sizeof(sockaddr_in);
501  
    }
501  
    }
502  
    else
502  
    else
503  
    {
503  
    {
504  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
504  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
505  
        std::memcpy(&ss, &sa, sizeof(sa));
505  
        std::memcpy(&ss, &sa, sizeof(sa));
506  
        ss_len = sizeof(sockaddr_in6);
506  
        ss_len = sizeof(sockaddr_in6);
507  
    }
507  
    }
508  

508  

509  
    char host[NI_MAXHOST];
509  
    char host[NI_MAXHOST];
510  
    char service[NI_MAXSERV];
510  
    char service[NI_MAXSERV];
511  

511  

512  
    int result = ::getnameinfo(
512  
    int result = ::getnameinfo(
513  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
513  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
514  
        sizeof(service),
514  
        sizeof(service),
515  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
515  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
516  

516  

517  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
517  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
518  
    {
518  
    {
519  
        if (result == 0)
519  
        if (result == 0)
520  
        {
520  
        {
521  
            self->reverse_op_.stored_host    = host;
521  
            self->reverse_op_.stored_host    = host;
522  
            self->reverse_op_.stored_service = service;
522  
            self->reverse_op_.stored_service = service;
523  
            self->reverse_op_.gai_error      = 0;
523  
            self->reverse_op_.gai_error      = 0;
524  
        }
524  
        }
525  
        else
525  
        else
526  
        {
526  
        {
527  
            self->reverse_op_.gai_error = result;
527  
            self->reverse_op_.gai_error = result;
528  
        }
528  
        }
529  
    }
529  
    }
530  

530  

531  
    // Move ref to stack before post — post may trigger destroy_impl
531  
    // Move ref to stack before post — post may trigger destroy_impl
532  
    // which erases the last shared_ptr, destroying *self (and *pw)
532  
    // which erases the last shared_ptr, destroying *self (and *pw)
533  
    auto ref = std::move(pw->ref_);
533  
    auto ref = std::move(pw->ref_);
534  
    self->svc_.post(&self->reverse_op_);
534  
    self->svc_.post(&self->reverse_op_);
535  
}
535  
}
536  

536  

537  
// posix_resolver_service implementation
537  
// posix_resolver_service implementation
538  

538  

539  
inline void
539  
inline void
540  
posix_resolver_service::shutdown()
540  
posix_resolver_service::shutdown()
541  
{
541  
{
542  
    std::lock_guard<std::mutex> lock(mutex_);
542  
    std::lock_guard<std::mutex> lock(mutex_);
543  

543  

544  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
544  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
545  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
545  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
546  
         impl       = resolver_list_.pop_front())
546  
         impl       = resolver_list_.pop_front())
547  
    {
547  
    {
548  
        impl->cancel();
548  
        impl->cancel();
549  
    }
549  
    }
550  

550  

551  
    // Clear the map which releases shared_ptrs.
551  
    // Clear the map which releases shared_ptrs.
552  
    // The thread pool service shuts down separately via
552  
    // The thread pool service shuts down separately via
553  
    // execution_context service ordering.
553  
    // execution_context service ordering.
554  
    resolver_ptrs_.clear();
554  
    resolver_ptrs_.clear();
555  
}
555  
}
556  

556  

557  
inline io_object::implementation*
557  
inline io_object::implementation*
558  
posix_resolver_service::construct()
558  
posix_resolver_service::construct()
559  
{
559  
{
560  
    auto ptr   = std::make_shared<posix_resolver>(*this);
560  
    auto ptr   = std::make_shared<posix_resolver>(*this);
561  
    auto* impl = ptr.get();
561  
    auto* impl = ptr.get();
562  

562  

563  
    {
563  
    {
564  
        std::lock_guard<std::mutex> lock(mutex_);
564  
        std::lock_guard<std::mutex> lock(mutex_);
565  
        resolver_list_.push_back(impl);
565  
        resolver_list_.push_back(impl);
566  
        resolver_ptrs_[impl] = std::move(ptr);
566  
        resolver_ptrs_[impl] = std::move(ptr);
567  
    }
567  
    }
568  

568  

569  
    return impl;
569  
    return impl;
570  
}
570  
}
571  

571  

572  
inline void
572  
inline void
573  
posix_resolver_service::destroy_impl(posix_resolver& impl)
573  
posix_resolver_service::destroy_impl(posix_resolver& impl)
574  
{
574  
{
575  
    std::lock_guard<std::mutex> lock(mutex_);
575  
    std::lock_guard<std::mutex> lock(mutex_);
576  
    resolver_list_.remove(&impl);
576  
    resolver_list_.remove(&impl);
577  
    resolver_ptrs_.erase(&impl);
577  
    resolver_ptrs_.erase(&impl);
578  
}
578  
}
579  

579  

580  
inline void
580  
inline void
581  
posix_resolver_service::post(scheduler_op* op)
581  
posix_resolver_service::post(scheduler_op* op)
582  
{
582  
{
583  
    sched_->post(op);
583  
    sched_->post(op);
584  
}
584  
}
585  

585  

586  
inline void
586  
inline void
587  
posix_resolver_service::work_started() noexcept
587  
posix_resolver_service::work_started() noexcept
588  
{
588  
{
589  
    sched_->work_started();
589  
    sched_->work_started();
590  
}
590  
}
591  

591  

592  
inline void
592  
inline void
593  
posix_resolver_service::work_finished() noexcept
593  
posix_resolver_service::work_finished() noexcept
594  
{
594  
{
595  
    sched_->work_finished();
595  
    sched_->work_finished();
596  
}
596  
}
597  

597  

598  
// Free function to get/create the resolver service
598  
// Free function to get/create the resolver service
599  

599  

600  
inline posix_resolver_service&
600  
inline posix_resolver_service&
601  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
601  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
602  
{
602  
{
603  
    return ctx.make_service<posix_resolver_service>(sched);
603  
    return ctx.make_service<posix_resolver_service>(sched);
604  
}
604  
}
605  

605  

606  
} // namespace boost::corosio::detail
606  
} // namespace boost::corosio::detail
607  

607  

608  
#endif // BOOST_COROSIO_POSIX
608  
#endif // BOOST_COROSIO_POSIX
609  

609  

610  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
610  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP