File: asio.hpp

package info (click to toggle)
openvpn3-client 25%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,276 kB
  • sloc: cpp: 190,085; python: 7,218; ansic: 1,866; sh: 1,361; java: 402; lisp: 81; makefile: 17
file content (155 lines) | stat: -rw-r--r-- 4,816 bytes parent folder | download
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 */