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  
    dispatch_coro(ex, h).resume();
268  
    dispatch_coro(ex, h).resume();
269  
}
269  
}
270  

270  

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

276  

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

282  

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

288  

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

292  

293  
// posix_resolver::reverse_resolve_op implementation
293  
// posix_resolver::reverse_resolve_op implementation
294  

294  

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

308  

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

313  

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

315  

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

325  

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

331  

332  
    impl->svc_.work_finished();
332  
    impl->svc_.work_finished();
333  
    dispatch_coro(ex, h).resume();
333  
    dispatch_coro(ex, h).resume();
334  
}
334  
}
335  

335  

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

341  

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

347  

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

353  

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

357  

358  
// posix_resolver implementation
358  
// posix_resolver implementation
359  

359  

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

382  

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

385  

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

399  

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

420  

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

423  

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

437  

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

444  

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

450  

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

455  

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

461  

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

475  

476  
    if (ai)
476  
    if (ai)
477  
        ::freeaddrinfo(ai);
477  
        ::freeaddrinfo(ai);
478  

478  

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

484  

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

490  

491  
    sockaddr_storage ss{};
491  
    sockaddr_storage ss{};
492  
    socklen_t ss_len;
492  
    socklen_t ss_len;
493  

493  

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

506  

507  
    char host[NI_MAXHOST];
507  
    char host[NI_MAXHOST];
508  
    char service[NI_MAXSERV];
508  
    char service[NI_MAXSERV];
509  

509  

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

514  

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

528  

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

534  

535  
// posix_resolver_service implementation
535  
// posix_resolver_service implementation
536  

536  

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

541  

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

548  

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

554  

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

560  

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

566  

567  
    return impl;
567  
    return impl;
568  
}
568  
}
569  

569  

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

577  

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

583  

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

589  

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

595  

596  
// Free function to get/create the resolver service
596  
// Free function to get/create the resolver service
597  

597  

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

603  

604  
} // namespace boost::corosio::detail
604  
} // namespace boost::corosio::detail
605  

605  

606  
#endif // BOOST_COROSIO_POSIX
606  
#endif // BOOST_COROSIO_POSIX
607  

607  

608  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
608  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP