1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
#ifndef OPENVPN_CLIENT_ASYNC_RESOLVE_ASIO_H
#define OPENVPN_CLIENT_ASYNC_RESOLVE_ASIO_H
#include <openvpn/io/io.hpp>
#include <openvpn/asio/asiowork.hpp>
#include <openvpn/common/bigmutex.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/hostport.hpp>
namespace openvpn {
template <typename RESOLVER_TYPE>
class AsyncResolvable
{
private:
typedef RCPtr<AsyncResolvable> Ptr;
class ResolveThread : public RC<thread_safe_refcount>
{
friend class AsyncResolvable<RESOLVER_TYPE>;
private:
typedef RCPtr<ResolveThread> Ptr;
openvpn_io::io_context &io_context;
AsyncResolvable<RESOLVER_TYPE> *parent;
std::atomic<bool> detached{false};
ResolveThread(openvpn_io::io_context &io_context_arg,
AsyncResolvable<RESOLVER_TYPE> *parent_arg,
const std::string &host,
const std::string &port)
: io_context(io_context_arg),
parent(parent_arg)
{
std::thread t([self = Ptr(this), host, port]()
{
openvpn_io::io_context io_context(1);
openvpn_io::error_code error;
RESOLVER_TYPE resolver(io_context);
typename RESOLVER_TYPE::results_type results;
results = resolver.resolve(host, port, error);
if (!self->is_detached())
{
self->post_callback(results, error);
} });
// detach the thread so that the client won't need to wait for
// it to join.
t.detach();
}
void detach()
{
detached.store(true, std::memory_order_relaxed);
parent = nullptr;
}
bool is_detached() const
{
return detached.load(std::memory_order_relaxed);
}
void post_callback(typename RESOLVER_TYPE::results_type results,
openvpn_io::error_code error)
{
openvpn_io::post(io_context, [self = Ptr(this), results, error]()
{
auto parent = self->parent;
if (!self->is_detached() && parent)
{
self->detach();
OPENVPN_ASYNC_HANDLER;
parent->resolve_callback(error, results);
} });
}
};
openvpn_io::io_context &io_context;
std::unique_ptr<AsioWork> asio_work;
typename ResolveThread::Ptr resolve_thread;
public:
using resolver_type = RESOLVER_TYPE;
using results_type = typename RESOLVER_TYPE::results_type;
AsyncResolvable(openvpn_io::io_context &io_context_arg)
: io_context(io_context_arg)
{
}
virtual ~AsyncResolvable()
{
async_resolve_cancel();
}
virtual void resolve_callback(const openvpn_io::error_code &error,
results_type results) = 0;
// mimic the asynchronous DNS resolution by performing a
// synchronous one in a detached thread.
//
// This strategy has the advantage of allowing the core to
// stop/exit without waiting for the getaddrinfo() (used
// internally) to terminate.
// Note: getaddrinfo() is non-interruptible by design.
//
// In other words, we are re-creating exactly what ASIO would
// normally do in case of async_resolve(), with the difference
// that here we have control over the resolving thread and we
// can easily detach it. Deatching the internal thread created
// by ASIO would not be feasible as it is not exposed.
virtual void async_resolve_name(const std::string &host, const std::string &port)
{
resolve_thread.reset(new ResolveThread(io_context, this, host, port));
}
// there might be nothing else in the main io_context queue
// right now, therefore we use AsioWork to prevent the loop
// from exiting while we perform the DNS resolution in the
// detached thread.
void async_resolve_lock()
{
asio_work.reset(new AsioWork(io_context));
}
// to be called by the child class when the core wants to stop
// and we don't need to wait for the detached thread any longer.
// It simulates a resolve abort
void async_resolve_cancel()
{
if (resolve_thread)
{
resolve_thread->detach();
resolve_thread.reset();
}
asio_work.reset();
}
};
} // namespace openvpn
#endif /* OPENVPN_CLIENT_ASYNC_RESOLVE_ASIO_H */
|