OpenVPN 3 Core Library
Loading...
Searching...
No Matches
cliconnect.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// This file implements the top-level connection logic for an OpenVPN client
13// connection. It is concerned with starting, stopping, pausing, and resuming
14// OpenVPN client connections. It deals with retrying a connection and handles
15// the connection timeout. It also deals with connection exceptions and understands
16// the difference between an exception that should halt any further reconnection
17// attempts (such as AUTH_FAILED), and other exceptions such as network errors
18// that would justify a retry.
19//
20// Some of the methods in the class (such as stop, pause, and reconnect) are often
21// called by another thread that is controlling the connection, therefore
22// thread-safe methods are provided where the thread-safe function posts a message
23// to the actual connection thread.
24//
25// In an OpenVPN client connection, the following object stack would be used:
26//
27// 1. class ClientConnect --
28// The top level object in an OpenVPN client connection.
29// 2. class ClientProto::Session --
30// The OpenVPN client protocol object.
31// 3. class ProtoContext --
32// The core OpenVPN protocol implementation that is common to both
33// client and server.
34// 4. ProtoStackBase<Packet> --
35// The lowest-level class that implements the basic functionality of
36// tunneling a protocol over a reliable or unreliable transport
37// layer, but isn't specific to OpenVPN per-se.
38
39#ifndef OPENVPN_CLIENT_CLICONNECT_H
40#define OPENVPN_CLIENT_CLICONNECT_H
41
42#include <memory>
43#include <utility>
44#include <chrono>
45using namespace std::chrono_literals;
46
48#include <openvpn/common/rc.hpp>
55
56namespace openvpn {
57
58// ClientConnect implements an "always-try-to-reconnect" approach, with remote
59// list rotation. Only gives up on auth failure or other fatal errors that
60// cannot be remedied by retrying.
64 public RC<thread_unsafe_refcount>
65{
66 public:
69
70 OPENVPN_SIMPLE_EXCEPTION(client_connect_unhandled_exception);
71
72 ClientConnect(openvpn_io::io_context &io_context_arg,
73 const ClientOptions::Ptr &client_options_arg)
74 : generation(0),
75 halt(false),
76 paused(false),
77 client_finalized(false),
78 dont_restart_(false),
79 lifecycle_started(false),
80 conn_timeout(client_options_arg->conn_timeout()),
81 io_context(io_context_arg),
82 client_options(client_options_arg),
83 server_poll_timer(io_context_arg),
84 restart_wait_timer(io_context_arg),
85 conn_timer(io_context_arg),
87 {
88 }
89
90 void start()
91 {
92 if (!client && !halt)
93 {
94 if (!test_network())
95 throw ErrorCode(Error::NETWORK_UNAVAILABLE, true, "Network Unavailable");
96
99 remote_list,
101 if (bulkres->work_available())
102 {
104 client_options->events().add_event(std::move(ev));
105 bulk_resolve = bulkres;
106 bulk_resolve->start(this); // asynchronous -- will call back to bulk_resolve_done
107 }
108 else
109 new_client();
110 }
111 }
112
114 {
115 if (!halt && client)
116 client->send_explicit_exit_notify();
117 }
118
120 {
122 // sleep(5); // simulate slow stop (comment out for production)
123 stop();
124 }
125
126 void stop()
127 {
128 if (!halt)
129 {
130 halt = true;
131 if (bulk_resolve)
132 bulk_resolve->cancel();
133 if (client)
134 {
135 client->tun_set_disconnect();
136 client->stop(false);
137 }
139 asio_work.reset();
140
142
144 {
146 if (lc)
147 lc->stop();
148 }
149
151 client_options->events().add_event(std::move(ev));
152#ifdef OPENVPN_IO_REQUIRES_STOP
153 io_context.stop();
154#endif
155 }
156 }
157
158 void stop_on_signal(const openvpn_io::error_code &error, int signal_number)
159 {
160 stop();
161 }
162
163 // like stop() but may be safely called by another thread
165 {
166 if (!halt)
167 openvpn_io::post(io_context, [self = Ptr(this)]()
168 {
170 self->graceful_stop(); });
171 }
172
173 void pause(const std::string &reason)
174 {
175 if (!halt && !paused)
176 {
177 paused = true;
178 if (client)
179 {
180 client->send_explicit_exit_notify();
181 client->stop(false);
183 }
185 asio_work.reset(new AsioWork(io_context));
187 client_options->events().add_event(std::move(ev));
189 }
190 }
191
192 void resume()
193 {
194 if (!halt && paused)
195 {
196 paused = false;
198 client_options->events().add_event(std::move(ev));
200 new_client();
201 }
202 }
203
204 void reconnect(int seconds)
205 {
206 if (!halt)
207 {
208 if (seconds < 0)
209 seconds = 0;
210 OPENVPN_LOG("Client terminated, reconnecting in " << seconds << "...");
211 server_poll_timer.cancel();
212 restart_wait_timer.expires_after(Time::Duration::seconds(seconds));
213 restart_wait_timer.async_wait([self = Ptr(this), gen = generation](const openvpn_io::error_code &error)
214 {
216 self->restart_wait_callback(gen, error); });
217 }
218 }
219
220 void thread_safe_pause(const std::string &reason)
221 {
222 if (!halt)
223 openvpn_io::post(io_context, [self = Ptr(this), reason]()
224 {
226 self->pause(reason); });
227 }
228
230 {
231 if (!halt)
232 openvpn_io::post(io_context, [self = Ptr(this)]()
233 {
235 self->resume(); });
236 }
237
238 void thread_safe_reconnect(int seconds)
239 {
240 if (!halt)
241 openvpn_io::post(io_context, [self = Ptr(this), seconds]()
242 {
244 self->reconnect(seconds); });
245 }
246
248 {
249 dont_restart_ = true;
250 }
251
252 void post_cc_msg(const std::string &msg)
253 {
254 if (!halt && client)
255 client->validate_and_post_cc_msg(msg);
256 }
257
258 void thread_safe_post_cc_msg(std::string msg)
259 {
260 if (!halt)
261 openvpn_io::post(io_context, [self = Ptr(this), msg = std::move(msg)]()
262 {
264 self->post_cc_msg(msg); });
265 }
266
267 void send_app_control_channel_msg(std::string protocol, std::string msg)
268 {
269 if (!halt && client)
270 client->post_app_control_message(std::move(protocol), std::move(msg));
271 }
279 template <typename... ArgsT>
280 void start_acc_certcheck(ArgsT &&...args)
281 {
282 if (!halt && client)
283 client->start_acc_certcheck(std::forward<ArgsT>(args)...);
284 }
285
286 void thread_safe_send_app_control_channel_msg(std::string protocol, std::string msg)
287 {
288 if (!halt)
289 {
290 openvpn_io::post(io_context, [self = Ptr(this), protocol = std::move(protocol), msg = std::move(msg)]()
291 {
293 self->send_app_control_channel_msg(protocol, msg); });
294 }
295 }
296
298 {
299 stop();
300 }
301
302 private:
304 {
305 if (!client_finalized)
306 {
307 client_options->finalize(false);
308 client_finalized = true;
309 }
310 }
311
312 virtual void bulk_resolve_done() override
313 {
314 if (!halt && generation == 0)
315 new_client();
316 }
317
319 {
320 restart_wait_timer.cancel();
321 server_poll_timer.cancel();
322 conn_timer.cancel();
323 conn_timer_pending = false;
324 }
325
326 void restart_wait_callback(unsigned int gen, const openvpn_io::error_code &e)
327 {
328 if (!e && gen == generation && !halt)
329 {
330 if (paused)
331 resume();
332 else
333 {
334 if (client)
335 client->send_explicit_exit_notify();
336 new_client();
337 }
338 }
339 }
340
341 void server_poll_callback(unsigned int gen, const openvpn_io::error_code &e)
342 {
343 if (!e && gen == generation && !halt && !client->first_packet_received())
344 {
345 OPENVPN_LOG("Server poll timeout, trying next remote entry...");
346 new_client();
347 }
348 }
349
350 void conn_timer_callback(unsigned int gen, const openvpn_io::error_code &e)
351 {
352 if (!e && !halt)
353 {
356 {
357 // go into pause state instead of disconnect
358 pause("");
359 }
360 else
361 {
363 client_options->events().add_event(std::move(ev));
364 stop();
365 }
366 }
367 }
368
369 void conn_timer_start(int timeout)
370 {
371 if (!conn_timer_pending && timeout > 0)
372 {
373 conn_timer.expires_after(Time::Duration::seconds(timeout));
374 conn_timer.async_wait([self = Ptr(this), gen = generation](const openvpn_io::error_code &error)
375 {
377 self->conn_timer_callback(gen, error); });
378 conn_timer_pending = true;
379 }
380 }
381
382 bool test_network() const
383 {
385 if (lc)
386 {
387 if (!lc->network_available())
388 return false;
389 }
390 return true;
391 }
392
393 virtual void client_proto_connected() override
394 {
395 conn_timer.cancel();
396 conn_timer_pending = false;
397
398 // Monitor connection lifecycle notifications, such as sleep,
399 // wakeup, network-unavailable, and network-available.
400 // Not all platforms define a lifecycle object. Some platforms
401 // such as Android and iOS manage lifecycle notifications
402 // in the UI, and they call pause(), resume(), reconnect(), etc.
403 // as needed using the main ovpncli API.
405 {
406 ClientLifeCycle *lc = client_options->lifecycle(); // lifecycle is defined by platform, and may be NULL
407 if (lc)
408 {
409 lc->start(this);
410 lifecycle_started = true;
411 }
412 }
413 }
414
416 {
417 // Try to re-lookup potentially outdated RemoteList::Items
418 if (bulk_resolve)
419 bulk_resolve->start(this);
420 }
421
422 void queue_restart(std::chrono::milliseconds delay = default_delay_)
423 {
424 OPENVPN_LOG("Client terminated, restarting in " << delay.count() << " ms...");
425 server_poll_timer.cancel();
427 restart_wait_timer.expires_after(Time::Duration::milliseconds(delay));
428 restart_wait_timer.async_wait([self = Ptr(this), gen = generation](const openvpn_io::error_code &error)
429 {
431 self->restart_wait_callback(gen, error); });
432 }
433
434 virtual void client_proto_auth_pending_timeout(int timeout) override
435 {
437 {
438 auto timer_left = std::chrono::duration_cast<std::chrono::seconds>(conn_timer.expiry() - AsioTimer::clock_type::now()).count();
439 if (timer_left < timeout)
440 {
441 OPENVPN_LOG("Extending connection timeout from " << timer_left << " to " << timeout << " for pending authentication");
442 conn_timer.cancel();
443 conn_timer_pending = false;
444 conn_timer_start(timeout);
445 }
446 }
447 }
448
449
450 template <typename ErrorClass>
452 {
453 add_error_and_stop<ErrorClass>(client->fatal(), client->fatal_reason());
454 }
455
456
457 template <typename ErrorClass>
458 void add_error_and_stop(const int error_code, const std::string &fatal_reason)
459 {
460 ClientEvent::Base::Ptr ev = new ErrorClass{fatal_reason};
461 client_options->events().add_event(std::move(ev));
462 client_options->stats().error(error_code);
463 stop();
464 }
465
466 template <typename ErrorClass>
467 void add_error_and_stop(const int error_code)
468 {
469 ClientEvent::Base::Ptr ev = new ErrorClass{};
470 client_options->events().add_event(std::move(ev));
471 client_options->stats().error(error_code);
472 stop();
473 }
474
475 template <typename ErrorClass>
476 void add_error_and_restart(std::chrono::milliseconds delay, const std::string &fatal_reason)
477 {
478 ClientEvent::Base::Ptr ev = new ErrorClass{fatal_reason};
479 client_options->events().add_event(std::move(ev));
481 queue_restart(delay);
482 }
483
484 template <typename ErrorClass>
485 void add_error_and_restart(std::chrono::milliseconds delay)
486 {
487 ClientEvent::Base::Ptr ev = new ErrorClass{};
488 client_options->events().add_event(std::move(ev));
490 queue_restart(delay);
491 }
492
493 virtual void client_proto_terminate() override
494 {
495 if (!halt)
496 {
497 if (dont_restart_)
498 {
499 stop();
500 }
501 else
502 {
503 auto fatal_code = client->fatal();
504 auto fatal_reason = client->fatal_reason();
505
506 switch (fatal_code)
507 {
508 case Error::UNDEF: // means that there wasn't a fatal error
509 {
510 std::chrono::duration client_delay = client->reconnect_delay();
511 queue_restart(client_delay.count() > 0 ? client_delay : default_delay_);
512 }
513 break;
514
515 // Errors below will cause the client to NOT retry the connection,
516 // or otherwise give the error special handling.
517
520 handle_auth_failed(fatal_code, fatal_reason);
521 break;
523 add_error_and_stop<ClientEvent::TunSetupFailed>(client.get());
524 break;
526 add_error_and_stop<ClientEvent::TunSetupFailed>(client.get());
527 break;
529 add_error_and_stop<ClientEvent::TunIfaceCreate>(client.get());
530 break;
532 add_error_and_restart<ClientEvent::TunIfaceDisabled>(5000ms, fatal_reason);
533 break;
535 add_error_and_stop<ClientEvent::ProxyError>(client.get());
536 break;
538 add_error_and_stop<ClientEvent::ProxyNeedCreds>(client.get());
539 break;
541 add_error_and_stop<ClientEvent::CertVerifyFail>(client.get());
542 break;
544 add_error_and_stop<ClientEvent::TLSVersionMinFail>(fatal_code);
545 break;
547 add_error_and_stop<ClientEvent::ClientHalt>(client.get());
548 break;
550 add_error_and_restart<ClientEvent::ClientRestart>(5000ms, fatal_reason);
551 break;
553 // explicit exit notify is sent earlier by
554 // ClientProto::Session::inactive_callback()
555 add_error_and_stop<ClientEvent::InactiveTimeout>(fatal_code);
556 break;
558 add_error_and_restart<ClientEvent::TransportError>(5000ms, fatal_reason);
559 break;
560 case Error::TUN_ERROR:
561 add_error_and_restart<ClientEvent::TunError>(5000ms, fatal_reason);
562 break;
563 case Error::TUN_HALT:
564 add_error_and_stop<ClientEvent::TunHalt>(client.get());
565 break;
566 case Error::RELAY:
567 transport_factory_relay = client->transport_factory_relay();
568 add_error_and_restart<ClientEvent::Relay>(0ms);
569 break;
571 add_error_and_stop<ClientEvent::RelayError>(client.get());
572 break;
574 add_error_and_stop<ClientEvent::CompressError>(client.get());
575 break;
577 add_error_and_stop<ClientEvent::NtlmMissingCryptoError>(client.get());
578 break;
580 add_error_and_stop<ClientEvent::TLSAlertProtocolVersion>(fatal_code);
581 break;
583 add_error_and_stop<ClientEvent::TLSSigAlgDisallowedOrUnsupported>(fatal_code);
584 break;
586 add_error_and_stop<ClientEvent::TLSAlertProtocolUnknownCA>(fatal_code);
587 break;
589 add_error_and_stop<ClientEvent::TLSAlertMisc>(fatal_code, fatal_reason);
590 break;
592 add_error_and_stop<ClientEvent::TLSAlertHandshakeFailure>(fatal_code);
593 break;
595 add_error_and_stop<ClientEvent::TLSAlertCertificateExpire>(fatal_code);
596 break;
598 add_error_and_stop<ClientEvent::TLSAlertCertificateRevoked>(fatal_code);
599 break;
601 add_error_and_stop<ClientEvent::TLSAlertBadCertificate>(fatal_code);
602 break;
604 add_error_and_stop<ClientEvent::TLSAlertUnsupportedCertificate>(fatal_code);
605 break;
607 {
609 client_options->events().add_event(std::move(ev));
611 stop();
612 }
613 break;
614 default:
615 throw client_connect_unhandled_exception();
616 }
617 }
618 }
619 }
620
621 void handle_auth_failed(const int error_code, const std::string &reason)
622 {
623 if (ChallengeResponse::is_dynamic(reason)) // dynamic challenge/response?
624 {
626 client_options->events().add_event(std::move(ev));
627 stop();
628 }
629 else
630 {
632 if (error_code == Error::SESSION_EXPIRED)
633 ev = new ClientEvent::SessionExpired(reason);
634 else
635 ev = new ClientEvent::AuthFailed(reason);
636 client_options->events().add_event(std::move(ev));
637 client_options->stats().error(error_code);
639 queue_restart(5000ms);
640 else
641 stop();
642 }
643 }
644
646 {
647 // Make sure generation is > 0 in case of overflow
648 if (++generation == 0)
649 ++generation;
650
652 asio_work.reset(new AsioWork(io_context));
653 else
654 asio_work.reset();
655
657 if (client)
658 {
659 advance_type = client->advance_type();
660 client->stop(false);
662 }
664 {
666 client_options->events().add_event(std::move(ev));
668 if (!(client && client->reached_connected_state()))
669 client_options->next(advance_type);
670 else
672 }
673
674 // client_config in cliopt.hpp
676 client.reset(new Client(io_context, *cli_config, this)); // build ClientProto::Session from cliproto.hpp
677 client_finalized = false;
678
679 // relay?
681 {
682 client->transport_factory_override(std::move(transport_factory_relay));
684 }
685
686 restart_wait_timer.cancel();
688 {
690 server_poll_timer.async_wait([self = Ptr(this), gen = generation](const openvpn_io::error_code &error)
691 {
693 self->server_poll_callback(gen, error); });
694 }
696 client->start();
697 }
698
699 // ClientLifeCycle::NotifyCallback callbacks
700
701 virtual void cln_stop() override
702 {
704 }
705
706 virtual void cln_pause(const std::string &reason) override
707 {
708 thread_safe_pause(reason);
709 }
710
711 virtual void cln_resume() override
712 {
714 }
715
716 virtual void cln_reconnect(int seconds) override
717 {
718 thread_safe_reconnect(seconds);
719 }
720
721 unsigned int generation;
722 bool halt;
723 bool paused;
728 openvpn_io::io_context &io_context;
736 std::unique_ptr<AsioWork> asio_work;
738
739 static constexpr std::chrono::milliseconds default_delay_ = 2000ms;
740};
741
742} // namespace openvpn
743
744#endif
#define OPENVPN_ASYNC_HANDLER
Definition bigmutex.hpp:36
std::size_t expires_after(const Time::Duration &d)
Definition asiotimer.hpp:69
static bool is_dynamic(const std::string &s)
Definition cr.hpp:114
virtual void client_proto_auth_pending_timeout(int timeout) override
virtual void cln_stop() override
void thread_safe_post_cc_msg(std::string msg)
TransportClientFactory::Ptr transport_factory_relay
ClientOptions::Client Client
void post_cc_msg(const std::string &msg)
virtual void client_proto_terminate() override
virtual void cln_pause(const std::string &reason) override
bool test_network() const
virtual void cln_reconnect(int seconds) override
virtual void client_proto_connected() override
void stop_on_signal(const openvpn_io::error_code &error, int signal_number)
RCPtr< ClientConnect > Ptr
void add_error_and_stop(const int error_code, const std::string &fatal_reason)
void client_proto_renegotiated() override
void restart_wait_callback(unsigned int gen, const openvpn_io::error_code &e)
RemoteList::BulkResolve::Ptr bulk_resolve
void queue_restart(std::chrono::milliseconds delay=default_delay_)
void handle_auth_failed(const int error_code, const std::string &reason)
OPENVPN_SIMPLE_EXCEPTION(client_connect_unhandled_exception)
void add_error_and_stop(const Client *client)
void server_poll_callback(unsigned int gen, const openvpn_io::error_code &e)
void thread_safe_reconnect(int seconds)
openvpn_io::io_context & io_context
void send_app_control_channel_msg(std::string protocol, std::string msg)
virtual void cln_resume() override
void start_acc_certcheck(ArgsT &&...args)
Passes the given arguments through to start_acc_certcheck.
virtual void bulk_resolve_done() override
void thread_safe_pause(const std::string &reason)
void pause(const std::string &reason)
void thread_safe_send_app_control_channel_msg(std::string protocol, std::string msg)
void conn_timer_callback(unsigned int gen, const openvpn_io::error_code &e)
void add_error_and_stop(const int error_code)
ClientOptions::Ptr client_options
static constexpr std::chrono::milliseconds default_delay_
void add_error_and_restart(std::chrono::milliseconds delay, const std::string &fatal_reason)
std::unique_ptr< AsioWork > asio_work
ClientConnect(openvpn_io::io_context &io_context_arg, const ClientOptions::Ptr &client_options_arg)
void reconnect(int seconds)
void conn_timer_start(int timeout)
void add_error_and_restart(std::chrono::milliseconds delay)
virtual void add_event(Base::Ptr event)=0
virtual void start(NotifyCallback *)=0
virtual bool network_available()=0
virtual void stop()=0
const SessionStats::Ptr & stats_ptr() const
Definition cliopt.hpp:1221
void finalize(const bool disconnected)
Definition cliopt.hpp:1267
bool server_poll_timeout_enabled() const
Definition cliopt.hpp:1207
bool asio_work_always_on() const
Definition cliopt.hpp:1239
SessionStats & stats()
Definition cliopt.hpp:1217
bool pause_on_connection_timeout()
Definition cliopt.hpp:1128
void next(RemoteList::Advance type)
Definition cliopt.hpp:1112
ClientEvent::Queue & events()
Definition cliopt.hpp:1225
ClientLifeCycle * lifecycle()
Definition cliopt.hpp:1229
Time::Duration server_poll_timeout() const
Definition cliopt.hpp:1212
bool retry_on_auth_failed() const
Definition cliopt.hpp:1136
RemoteList::Ptr remote_list_precache() const
Definition cliopt.hpp:1244
Client::Config::Ptr client_config(const bool relay_mode)
Definition cliopt.hpp:1148
The smart pointer class.
Definition rc.hpp:119
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
Definition rc.hpp:321
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:912
virtual void error(const size_t type, const std::string *text=nullptr)
#define OPENVPN_LOG(args)
@ TLS_ALERT_CERTIFICATE_REVOKED
Definition error.hpp:73
@ NTLM_MISSING_CRYPTO
Definition error.hpp:93
@ CONNECTION_TIMEOUT
Definition error.hpp:63
@ TLS_ALERT_BAD_CERTIFICATE
Definition error.hpp:74
@ TLS_ALERT_CERTIFICATE_EXPIRED
Definition error.hpp:72
@ TLS_ALERT_UNSUPPORTED_CERTIFICATE
Definition error.hpp:75
@ TUN_REGISTER_RINGS_ERROR
Definition error.hpp:44
@ NETWORK_UNAVAILABLE
Definition error.hpp:27
@ TUN_IFACE_DISABLED
Definition error.hpp:42
@ TLS_ALERT_PROTOCOL_VERSION
Definition error.hpp:68
@ TLS_ALERT_HANDSHAKE_FAILURE
Definition error.hpp:70
@ TLS_SIGALG_DISALLOWED_OR_UNSUPPORTED
Definition error.hpp:67
@ TLS_ALERT_UNKNOWN_CA
Definition error.hpp:69
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
#define msg(flags,...)