0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 12:12:15 +02:00
openvpn3/openvpn/client/cliproto.hpp

944 lines
26 KiB
C++
Raw Normal View History

// 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.
//
2015-01-06 20:56:21 +01:00
// Copyright (C) 2012-2015 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_CLIENT_CLIPROTO_H
#define OPENVPN_CLIENT_CLIPROTO_H
// This is a middle-layer object in the OpenVPN client protocol stack.
// It is above the general OpenVPN protocol implementation in
// class ProtoContext but below the top
// level object in a client connect (ClientConnect). See ClientConnect for
// a fuller description of the full client stack.
//
// This layer deals with setting up an OpenVPN client connection:
//
// 1. handles creation of transport-layer handler via TransportClientFactory
// 2. handles creation of tun-layer handler via TunClientFactory
// 3. handles sending PUSH_REQUEST to server and processing reply of server-pushed options
// 4. manages the underlying OpenVPN protocol object (class ProtoContext)
// 5. handles timers on behalf of the underlying OpenVPN protocol object
// 6. acts as an exception dispatcher for errors occuring in any of the underlying layers
#include <string>
#include <vector>
#include <algorithm> // for std::min
#include <cstdint> // for std::uint...
#include <asio.hpp>
#include <openvpn/common/rc.hpp>
2015-05-17 21:17:24 +02:00
#include <openvpn/common/count.hpp>
#include <openvpn/common/string.hpp>
2016-01-27 09:32:48 +01:00
#include <openvpn/common/base64.hpp>
#include <openvpn/tun/client/tunbase.hpp>
#include <openvpn/transport/client/transbase.hpp>
#include <openvpn/options/continuation.hpp>
#include <openvpn/options/sanitize.hpp>
#include <openvpn/client/clievent.hpp>
#include <openvpn/client/clicreds.hpp>
#include <openvpn/client/cliconstants.hpp>
2012-11-26 02:51:24 +01:00
#include <openvpn/client/clihalt.hpp>
#include <openvpn/time/asiotimer.hpp>
#include <openvpn/time/coarsetime.hpp>
#include <openvpn/time/durhelper.hpp>
2012-11-14 17:41:33 +01:00
#include <openvpn/error/excode.hpp>
#include <openvpn/ssl/proto.hpp>
#ifdef OPENVPN_DEBUG_CLIPROTO
#define OPENVPN_LOG_CLIPROTO(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_CLIPROTO(x)
#endif
namespace openvpn {
namespace ClientProto {
struct NotifyCallback {
virtual void client_proto_terminate() = 0;
virtual void client_proto_connected() {}
};
class Session : ProtoContext,
TransportClientParent,
TunClientParent,
public RC<thread_unsafe_refcount>
{
typedef ProtoContext Base;
typedef Base::PacketType PacketType;
using Base::now;
using Base::stat;
public:
typedef RCPtr<Session> Ptr;
typedef Base::Config ProtoConfig;
OPENVPN_EXCEPTION(client_exception);
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
OPENVPN_EXCEPTION(client_halt_restart);
OPENVPN_EXCEPTION(tun_exception);
OPENVPN_EXCEPTION(transport_exception);
OPENVPN_EXCEPTION(max_pushed_options_exceeded);
OPENVPN_SIMPLE_EXCEPTION(session_invalidated);
OPENVPN_SIMPLE_EXCEPTION(authentication_failed);
2013-05-25 03:19:50 +02:00
OPENVPN_SIMPLE_EXCEPTION(inactive_timer_expired);
OPENVPN_EXCEPTION(proxy_exception);
struct Config : public RC<thread_unsafe_refcount>
{
typedef RCPtr<Config> Ptr;
Config()
: pushed_options_limit("server-pushed options data too large",
ProfileParseLimits::MAX_PUSH_SIZE,
ProfileParseLimits::OPT_OVERHEAD,
ProfileParseLimits::TERM_OVERHEAD,
2012-11-14 03:35:50 +01:00
0,
ProfileParseLimits::MAX_DIRECTIVE_SIZE)
{}
ProtoConfig::Ptr proto_context_config;
ProtoContextOptions::Ptr proto_context_options;
PushOptionsBase::Ptr push_base;
TransportClientFactory::Ptr transport_factory;
TunClientFactory::Ptr tun_factory;
SessionStats::Ptr cli_stats;
ClientEvent::Queue::Ptr cli_events;
ClientCreds::Ptr creds;
OptionList::Limits pushed_options_limit;
OptionList::FilterBase::Ptr pushed_options_filter;
unsigned int tcp_queue_limit = 0;
bool echo = false;
bool info = false;
bool autologin_sessions = false;
};
2015-06-30 08:05:37 +02:00
Session(asio::io_context& io_context_arg,
const Config& config,
NotifyCallback* notify_callback_arg)
: Base(config.proto_context_config, config.cli_stats),
2015-06-30 08:05:37 +02:00
io_context(io_context_arg),
transport_factory(config.transport_factory),
tun_factory(config.tun_factory),
tcp_queue_limit(config.tcp_queue_limit),
notify_callback(notify_callback_arg),
2015-06-30 08:05:37 +02:00
housekeeping_timer(io_context_arg),
push_request_timer(io_context_arg),
received_options(config.push_base),
creds(config.creds),
proto_context_options(config.proto_context_options),
cli_stats(config.cli_stats),
cli_events(config.cli_events),
echo(config.echo),
info(config.info),
autologin_sessions(config.autologin_sessions),
pushed_options_limit(config.pushed_options_limit),
2013-05-25 03:19:50 +02:00
pushed_options_filter(config.pushed_options_filter),
inactive_timer(io_context_arg)
{
#ifdef OPENVPN_PACKET_LOG
packet_log.open(OPENVPN_PACKET_LOG, std::ios::binary);
if (!packet_log)
OPENVPN_THROW(open_file_error, "cannot open packet log for output: " << OPENVPN_PACKET_LOG);
#endif
Base::update_now();
Base::reset();
//Base::enable_strict_openvpn_2x();
}
bool first_packet_received() const { return first_packet_received_; }
void start()
{
if (!halt)
{
Base::update_now();
// coarse wakeup range
housekeeping_schedule.init(Time::Duration::binary_ms(512), Time::Duration::binary_ms(1024));
// initialize transport-layer packet handler
2015-06-30 08:05:37 +02:00
transport = transport_factory->new_transport_client_obj(io_context, *this);
transport_has_send_queue = transport->transport_has_send_queue();
transport->transport_start();
}
}
2012-02-27 09:16:27 +01:00
void send_explicit_exit_notify()
{
if (!halt)
Base::send_explicit_exit_notify();
}
void tun_set_disconnect()
{
if (tun)
tun->set_disconnect();
}
void stop(const bool call_terminate_callback)
{
if (!halt)
{
halt = true;
housekeeping_timer.cancel();
push_request_timer.cancel();
2013-05-25 03:19:50 +02:00
inactive_timer.cancel();
if (notify_callback && call_terminate_callback)
notify_callback->client_proto_terminate();
if (tun)
tun->stop(); // call after client_proto_terminate() so it can call back to tun_set_disconnect
if (transport)
transport->stop();
}
}
void stop_on_signal(const asio::error_code& error, int signal_number)
{
stop(true);
}
bool reached_connected_state() const { return connected_; }
// If fatal() returns something other than Error::UNDEF, it
// is intended to flag the higher levels (cliconnect.hpp)
// that special handling is required. This handling might include
// considering the error to be fatal and stopping future connect
// retries, or emitting a special event. See cliconnect.hpp
// for actual implementation.
Error::Type fatal() const { return fatal_; }
const std::string& fatal_reason() const { return fatal_reason_; }
2012-02-15 19:19:34 +01:00
virtual ~Session()
{
stop(false);
}
private:
virtual bool transport_is_openvpn_protocol()
{
return true;
}
// transport obj calls here with incoming packets
virtual void transport_recv(BufferAllocated& buf)
{
try {
OPENVPN_LOG_CLIPROTO("Transport RECV " << server_endpoint_render() << ' ' << Base::dump_packet(buf));
// update current time
Base::update_now();
// update last packet received
stat().update_last_packet_received(now());
// log connecting event (only on first packet received)
if (!first_packet_received_)
{
ClientEvent::Base::Ptr ev = new ClientEvent::Connecting();
cli_events->add_event(std::move(ev));
first_packet_received_ = true;
}
// get packet type
Base::PacketType pt = Base::packet_type(buf);
// process packet
if (pt.is_data())
{
// data packet
Base::data_decrypt(pt, buf);
if (buf.size())
{
#ifdef OPENVPN_PACKET_LOG
log_packet(buf, false);
#endif
// make packet appear as incoming on tun interface
if (tun)
{
OPENVPN_LOG_CLIPROTO("TUN send, size=" << buf.size());
tun->tun_send(buf);
}
}
// do a lightweight flush
Base::flush(false);
}
else if (pt.is_control())
{
// control packet
Base::control_net_recv(pt, std::move(buf));
// do a full flush
Base::flush(true);
}
// schedule housekeeping wakeup
set_housekeeping_timer();
}
catch (const ExceptionCode& e)
{
if (e.code_defined())
{
if (e.fatal())
transport_error((Error::Type)e.code(), e.what());
else
cli_stats->error((Error::Type)e.code());
}
else
2012-11-14 17:41:33 +01:00
process_exception(e, "transport_recv_excode");
}
catch (const std::exception& e)
{
process_exception(e, "transport_recv");
}
}
virtual void transport_needs_send()
{
}
// tun i/o driver calls here with incoming packets
virtual void tun_recv(BufferAllocated& buf)
{
try {
OPENVPN_LOG_CLIPROTO("TUN recv, size=" << buf.size());
// update current time
Base::update_now();
// log packet
#ifdef OPENVPN_PACKET_LOG
log_packet(buf, true);
#endif
// if transport layer has an output queue, check if it's full
if (transport_has_send_queue)
{
if (transport->transport_send_queue_size() > tcp_queue_limit)
{
buf.reset_size(); // queue full, drop packet
cli_stats->error(Error::TCP_OVERFLOW);
}
}
// encrypt packet
if (buf.size())
{
Base::data_encrypt(buf);
if (buf.size())
{
// send packet via transport to destination
OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(buf));
if (transport->transport_send(buf))
Base::update_last_sent();
else if (halt)
return;
}
}
// do a lightweight flush
Base::flush(false);
// schedule housekeeping wakeup
set_housekeeping_timer();
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "tun_recv");
}
}
// Return true if keepalive parameter(s) are enabled.
virtual bool is_keepalive_enabled() const
{
return Base::is_keepalive_enabled();
}
// Disable keepalive for rest of session, but fetch
// the keepalive parameters (in seconds).
virtual void disable_keepalive(unsigned int& keepalive_ping,
unsigned int& keepalive_timeout)
{
Base::disable_keepalive(keepalive_ping, keepalive_timeout);
}
virtual void ip_hole_punch(const IP::Addr& addr)
{
tun_factory->ip_hole_punch(addr);
}
virtual void transport_pre_resolve()
{
ClientEvent::Base::Ptr ev = new ClientEvent::Resolve();
cli_events->add_event(std::move(ev));
}
std::string server_endpoint_render()
{
std::string server_host, server_port, server_proto, server_ip;
transport->server_endpoint_info(server_host, server_port, server_proto, server_ip);
std::ostringstream out;
out << '[' << server_host << "]:" << server_port << " (" << server_ip << ") via " << server_proto;
return out.str();
}
virtual void transport_wait_proxy()
{
ClientEvent::Base::Ptr ev = new ClientEvent::WaitProxy();
cli_events->add_event(std::move(ev));
}
virtual void transport_wait()
{
ClientEvent::Base::Ptr ev = new ClientEvent::Wait();
cli_events->add_event(std::move(ev));
}
virtual void transport_connecting()
{
try {
OPENVPN_LOG("Connecting to " << server_endpoint_render());
Base::start();
Base::flush(true);
set_housekeeping_timer();
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "transport_connecting");
}
}
virtual void transport_error(const Error::Type fatal_err, const std::string& err_text)
{
if (fatal_err != Error::UNDEF)
{
fatal_ = fatal_err;
fatal_reason_ = err_text;
}
if (notify_callback)
{
OPENVPN_LOG("Transport Error: " << err_text);
stop(true);
}
else
throw transport_exception(err_text);
}
virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text)
{
if (fatal_err != Error::UNDEF)
{
fatal_ = fatal_err;
fatal_reason_ = err_text;
}
if (notify_callback)
{
OPENVPN_LOG("Proxy Error: " << err_text);
stop(true);
}
else
throw proxy_exception(err_text);
}
void extract_auth_token(const OptionList& opt)
{
std::string username;
2016-01-27 09:32:48 +01:00
// auth-token-user
{
const Option* o = opt.get_ptr("auth-token-user");
if (o)
username = base64->decode(o->get(1, 256));
2016-01-27 09:32:48 +01:00
}
// auth-token
{
// if auth-token is present, use it as the password for future renegotiations
const Option* o = opt.get_ptr("auth-token");
if (o)
{
const std::string& sess_id = o->get(1, 256);
if (creds)
{
if (!username.empty())
OPENVPN_LOG("Session user: " << username);
#ifdef OPENVPN_SHOW_SESSION_TOKEN
2016-01-27 09:32:48 +01:00
OPENVPN_LOG("Session token: " << sess_id);
#else
2016-01-27 09:32:48 +01:00
OPENVPN_LOG("Session token: [redacted]");
#endif
creds->set_session_id(username, sess_id);
2016-01-27 09:32:48 +01:00
}
}
}
}
// proto base class calls here for control channel network sends
virtual void control_net_send(const Buffer& net_buf)
{
OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(net_buf));
if (transport->transport_send_const(net_buf))
Base::update_last_sent();
}
// proto base class calls here for app-level control-channel messages received
virtual void control_recv(BufferPtr&& app_bp)
{
2012-11-14 03:35:50 +01:00
const std::string msg = Unicode::utf8_printable(Base::template read_control_string<std::string>(*app_bp),
Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT);
2012-11-14 03:35:50 +01:00
//OPENVPN_LOG("SERVER: " << sanitize_control_message(msg));
if (!received_options.complete() && string::starts_with(msg, "PUSH_REPLY,"))
{
// parse the received options
received_options.add(OptionList::parse_from_csv_static(msg.substr(11), &pushed_options_limit),
pushed_options_filter.get());
if (received_options.complete())
{
// show options
OPENVPN_LOG("OPTIONS:" << std::endl << render_options_sanitized(received_options, Option::RENDER_PASS_FMT|Option::RENDER_NUMBER|Option::RENDER_BRACKET));
// process "echo" directives
if (echo)
process_echo(received_options);
// process auth-token
extract_auth_token(received_options);
// modify proto config (cipher, auth, and compression methods)
Base::process_push(received_options, *proto_context_options);
// initialize tun/routing
2015-06-30 08:05:37 +02:00
tun = tun_factory->new_tun_client_obj(io_context, *this, transport.get());
tun->tun_start(received_options, *transport, Base::dc_settings());
// initialize data channel after pushed options have been processed
Base::init_data_channel();
// Allow ProtoContext to suggest an alignment adjustment
// hint for transport layer.
transport->reset_align_adjust(Base::align_adjust_hint());
2013-05-25 03:19:50 +02:00
// process "inactive" directive
process_inactive(received_options);
}
else
OPENVPN_LOG("Options continuation...");
}
else if (string::starts_with(msg, "AUTH_FAILED"))
{
std::string reason;
std::string log_reason;
// get reason (if it exists) for authentication failure
2012-02-15 19:19:34 +01:00
if (msg.length() >= 13)
reason = string::trim_left_copy(std::string(msg, 12));
// If session token problem (such as expiration), and we have a cached
// password, retry with it. Otherwise, fail without retry.
if (string::starts_with(reason, "SESSION:")
&& (autologin_sessions
|| (creds && creds->can_retry_auth_with_cached_password())))
{
log_reason = "SESSION_AUTH_FAILED";
}
else
{
fatal_ = Error::AUTH_FAILED;
fatal_reason_ = reason;
log_reason = "AUTH_FAILED";
}
if (notify_callback)
{
OPENVPN_LOG(log_reason);
stop(true);
}
else
throw authentication_failed();
}
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
else if (ClientHalt::match(msg))
{
const ClientHalt ch(msg, true);
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
process_halt_restart(ch);
}
else if (info && string::starts_with(msg, "INFO,"))
{
ClientEvent::Base::Ptr ev = new ClientEvent::Info(msg.substr(5));
cli_events->add_event(std::move(ev));
}
}
virtual void tun_pre_tun_config()
{
ClientEvent::Base::Ptr ev = new ClientEvent::AssignIP();
cli_events->add_event(std::move(ev));
}
virtual void tun_pre_route_config()
{
ClientEvent::Base::Ptr ev = new ClientEvent::AddRoutes();
cli_events->add_event(std::move(ev));
}
virtual void tun_connected()
{
OPENVPN_LOG("Connected via " + tun->tun_name());
ClientEvent::Connected::Ptr ev = new ClientEvent::Connected();
if (creds)
ev->user = creds->get_username();
transport->server_endpoint_info(ev->server_host, ev->server_port, ev->server_proto, ev->server_ip);
ev->vpn_ip4 = tun->vpn_ip4();
ev->vpn_ip6 = tun->vpn_ip6();
ev->vpn_gw4 = tun->vpn_gw4();
ev->vpn_gw6 = tun->vpn_gw6();
try {
2012-11-14 03:35:50 +01:00
std::string client_ip = received_options.get_optional("client-ip", 1, 256);
if (!client_ip.empty())
ev->client_ip = IP::Addr::validate(client_ip, "client-ip");
}
catch (const std::exception& e)
{
OPENVPN_LOG("Error parsing client-ip: " << e.what());
}
ev->tun_name = tun->tun_name();
cli_events->add_event(std::move(ev));
connected_ = true;
if (notify_callback)
notify_callback->client_proto_connected();
}
virtual void tun_error(const Error::Type fatal_err, const std::string& err_text)
{
if (fatal_err != Error::UNDEF)
{
fatal_ = fatal_err;
fatal_reason_ = err_text;
}
if (notify_callback)
{
OPENVPN_LOG("TUN Error: " << err_text);
stop(true);
}
else
throw tun_exception(err_text);
}
// proto base class calls here to get auth credentials
virtual void client_auth(Buffer& buf)
{
if (creds)
{
OPENVPN_LOG("Creds: " << creds->auth_info());
Base::write_auth_string(creds->get_username(), buf);
Base::write_auth_string(creds->get_password(), buf);
}
else
{
OPENVPN_LOG("Creds: None");
Base::write_empty_string(buf); // username
Base::write_empty_string(buf); // password
}
}
void send_push_request_callback(const Time::Duration& dur,
const asio::error_code& e)
{
try {
if (!e && !halt && !received_options.partial())
{
Base::update_now();
if (!sent_push_request)
{
ClientEvent::Base::Ptr ev = new ClientEvent::GetConfig();
cli_events->add_event(std::move(ev));
sent_push_request = true;
}
OPENVPN_LOG("Sending PUSH_REQUEST to server...");
Base::write_control_string(std::string("PUSH_REQUEST"));
Base::flush(true);
set_housekeeping_timer();
{
const Time::Duration newdur = std::min(dur + Time::Duration::seconds(1),
Time::Duration::seconds(3));
schedule_push_request_callback(newdur);
}
}
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "send_push_request_callback");
}
}
void schedule_push_request_callback(const Time::Duration& dur)
{
if (!received_options.partial())
{
push_request_timer.expires_at(now() + dur);
push_request_timer.async_wait([self=Ptr(this), dur](const asio::error_code& error)
{
self->send_push_request_callback(dur, error);
});
}
}
// base class calls here when primary session transitions to ACTIVE state
virtual void active()
{
OPENVPN_LOG("Session is ACTIVE");
schedule_push_request_callback(Time::Duration::seconds(0));
}
void housekeeping_callback(const asio::error_code& e)
{
try {
if (!e && !halt)
{
// update current time
Base::update_now();
housekeeping_schedule.reset();
Base::housekeeping();
if (Base::invalidated())
{
if (notify_callback)
{
OPENVPN_LOG("Session invalidated: " << Error::name(Base::invalidation_reason()));
stop(true);
}
else
throw session_invalidated();
}
set_housekeeping_timer();
}
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "housekeeping_callback");
}
}
void set_housekeeping_timer()
{
if (halt)
return;
Time next = Base::next_housekeeping();
if (!housekeeping_schedule.similar(next))
{
if (!next.is_infinite())
{
next.max(now());
housekeeping_schedule.reset(next);
housekeeping_timer.expires_at(next);
housekeeping_timer.async_wait([self=Ptr(this)](const asio::error_code& error)
{
self->housekeeping_callback(error);
});
}
else
{
housekeeping_timer.cancel();
}
}
}
void process_inactive(const OptionList& opt)
2013-05-25 03:19:50 +02:00
{
try {
const Option *o = load_duration_parm(inactive_duration, "inactive", opt, 1, false);
2013-05-25 03:19:50 +02:00
if (o)
{
if (o->size() >= 3)
inactive_bytes = parse_number_throw<unsigned int>(o->get(2, 16), "inactive bytes");
schedule_inactive_timer();
}
}
catch (const std::exception& e)
{
OPENVPN_LOG("Error parsing inactive: " << e.what());
}
}
void schedule_inactive_timer()
{
inactive_timer.expires_at(now() + inactive_duration);
inactive_timer.async_wait([self=Ptr(this)](const asio::error_code& error)
{
self->inactive_callback(error);
});
2013-05-25 03:19:50 +02:00
}
void inactive_callback(const asio::error_code& e) // fixme for DCO
2013-05-25 03:19:50 +02:00
{
try {
if (!e && !halt)
{
// update current time
Base::update_now();
const count_t sample = cli_stats->get_stat(SessionStats::TUN_BYTES_IN) + cli_stats->get_stat(SessionStats::TUN_BYTES_OUT);
const count_t delta = sample - inactive_last_sample;
//OPENVPN_LOG("*** INACTIVE SAMPLE " << delta << ' ' << inactive_bytes);
if (delta <= count_t(inactive_bytes))
{
fatal_ = Error::INACTIVE_TIMEOUT;
send_explicit_exit_notify();
if (notify_callback)
{
OPENVPN_LOG("inactive timer expired");
stop(true);
}
else
throw inactive_timer_expired();
}
else
{
inactive_last_sample = sample;
schedule_inactive_timer();
}
}
}
catch (const std::exception& e)
{
process_exception(e, "inactive_callback");
}
}
void process_echo(const OptionList& opt)
{
OptionList::IndexMap::const_iterator echo_opt = opt.map().find("echo");
if (echo_opt != opt.map().end())
{
for (OptionList::IndexList::const_iterator i = echo_opt->second.begin(); i != echo_opt->second.end(); ++i)
{
const Option& o = opt[*i];
o.touch();
const std::string& value = o.get(1, 512);
ClientEvent::Base::Ptr ev = new ClientEvent::Echo(value);
cli_events->add_event(std::move(ev));
}
}
}
2012-07-01 17:37:46 +02:00
void process_exception(const std::exception& e, const char *method_name)
{
if (notify_callback)
{
2012-07-01 17:37:46 +02:00
OPENVPN_LOG("Client exception in " << method_name << ": " << e.what());
stop(true);
}
else
throw client_exception(e.what());
}
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
void process_halt_restart(const ClientHalt& ch)
{
if (!ch.psid() && creds)
creds->can_retry_auth_with_cached_password(); // purge session ID
if (ch.restart())
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
fatal_ = Error::CLIENT_RESTART;
else
fatal_ = Error::CLIENT_HALT;
fatal_reason_ = ch.reason();
if (notify_callback)
{
OPENVPN_LOG("Client halt/restart: " << ch.render());
stop(true);
}
else
throw client_halt_restart(ch.render());
}
#ifdef OPENVPN_PACKET_LOG
void log_packet(const Buffer& buf, const bool out)
{
if (buf.size())
{
std::uint16_t len = buf.size() & 0x7FFF;
if (out)
len |= 0x8000;
packet_log.write((const char *)&len, sizeof(len));
packet_log.write((const char *)buf.c_data(), buf.size());
}
}
#endif
2015-06-30 08:05:37 +02:00
asio::io_context& io_context;
TransportClientFactory::Ptr transport_factory;
TransportClient::Ptr transport;
TunClientFactory::Ptr tun_factory;
TunClient::Ptr tun;
unsigned int tcp_queue_limit;
bool transport_has_send_queue = false;
NotifyCallback* notify_callback;
CoarseTime housekeeping_schedule;
AsioTimer housekeeping_timer;
AsioTimer push_request_timer;
bool halt = false;
OptionListContinuation received_options;
ClientCreds::Ptr creds;
ProtoContextOptions::Ptr proto_context_options;
iOS version: 1.0 Beta 17 Android version: 1.1 beta 1 More alignment of iOS and Android clients: * Normalized building of dependencies for Android and iOS: This build adds some new library dependencies: The library versions required are enumerated in ovpn3/lib-versions, currently: export BOOST_VERSION=boost_1_51_0 export OPENSSL_VERSION=openssl-1.0.1c export POLARSSL_VERSION=polarssl-1.1.4 export LZO_VERSION=lzo-2.06 To build, first mkdir ~/src/android and ~/src/mac if they don't already exist. Set the env var O3 to point to the ovpn3 dir, usually ~/src/ovpn3. Build on iOS: [set PATH to include NDK] cd ~/src/android $O3/scripts/android/build-boost $O3/scripts/android/build-minicrypto $O3/scripts/android/build-polarssl $O3/scripts/android/build-lzo Build on Android: [set PATH to include NDK] cd ~/src/android $O3/scripts/android/build-boost $O3/scripts/android/build-minicrypto $O3/scripts/android/build-polarssl $O3/scripts/android/build-lzo * Integrated Minicrypto library (an assembly language library of low-level crypto functions adapted from OpenSSL). * Added LZO compression with a preference/settings item to enable or disable. * Added special compression handling to support older servers that ignore compression handshake -- this will handle receiving compressed packets even if we didn't ask for them. * Normalized profile naming conventions. iOS changes: * Log tunnel performance stats immediately on disconnection of tunnel. Android changes: * Client now supports loading profiles as attachments opened from other apps. * Added Import Private Tunnel menu item, however current Private Tunnel download page needs to be adapted to fit requirements of Android download manager. * Enter key should advance to the next input field, or connect if entered from the last field. * Import from Access Server now provides the option to download autologin vs. userlogin profiles. * "About" page now shows copyright text for included libraries/content (except for LZO and PolarSSL which will presumably be commercially licensed).
2012-09-05 03:09:34 +02:00
bool first_packet_received_ = false;
bool sent_push_request = false;
SessionStats::Ptr cli_stats;
ClientEvent::Queue::Ptr cli_events;
bool connected_ = false;
bool echo;
bool info;
bool autologin_sessions;
Error::Type fatal_ = Error::UNDEF;
std::string fatal_reason_;
OptionList::Limits pushed_options_limit;
OptionList::FilterBase::Ptr pushed_options_filter;
2013-05-25 03:19:50 +02:00
AsioTimer inactive_timer;
Time::Duration inactive_duration;
unsigned int inactive_bytes = 0;
count_t inactive_last_sample = 0;
2013-05-25 03:19:50 +02:00
#ifdef OPENVPN_PACKET_LOG
std::ofstream packet_log;
#endif
};
}
}
#endif