Merge branch 'feature/update-dependencies' into develop

This commit is contained in:
Sergey Abramchuk
2017-04-15 12:15:07 +03:00
39 changed files with 773 additions and 223 deletions
@@ -83,7 +83,7 @@
// on Android and iOS, use TunBuilderBase abstraction // on Android and iOS, use TunBuilderBase abstraction
#include <openvpn/common/platform.hpp> #include <openvpn/common/platform.hpp>
#if (defined(OPENVPN_PLATFORM_ANDROID) || defined(OPENVPN_PLATFORM_IPHONE)) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_CUSTOM_TUN_FACTORY) #if (defined(OPENVPN_PLATFORM_ANDROID) || defined(OPENVPN_PLATFORM_IPHONE)) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_EXTERNAL_TUN_FACTORY)
#define USE_TUN_BUILDER #define USE_TUN_BUILDER
#endif #endif
@@ -92,6 +92,7 @@
#include <openvpn/common/platform_string.hpp> #include <openvpn/common/platform_string.hpp>
#include <openvpn/common/count.hpp> #include <openvpn/common/count.hpp>
#include <openvpn/asio/asiostop.hpp> #include <openvpn/asio/asiostop.hpp>
#include <openvpn/time/asiotimer.hpp>
#include <openvpn/client/cliconnect.hpp> #include <openvpn/client/cliconnect.hpp>
#include <openvpn/client/cliopthelper.hpp> #include <openvpn/client/cliopthelper.hpp>
#include <openvpn/options/merge.hpp> #include <openvpn/options/merge.hpp>
@@ -205,7 +206,8 @@ namespace openvpn {
// save connected event // save connected event
if (event->id() == ClientEvent::CONNECTED) if (event->id() == ClientEvent::CONNECTED)
last_connected = std::move(event); last_connected = std::move(event);
else if (event->id() == ClientEvent::DISCONNECTED)
parent->on_disconnect();
parent->event(ev); parent->event(ev);
} }
} }
@@ -341,6 +343,51 @@ namespace openvpn {
OpenVPNClient* parent = nullptr; OpenVPNClient* parent = nullptr;
}; };
class MyClockTick
{
public:
MyClockTick(openvpn_io::io_context& io_context,
OpenVPNClient* parent_arg,
const unsigned int ms)
: timer(io_context),
parent(parent_arg),
period(Time::Duration::milliseconds(ms))
{
}
void cancel()
{
timer.cancel();
}
void detach_from_parent()
{
parent = nullptr;
}
void schedule()
{
timer.expires_after(period);
timer.async_wait([this](const openvpn_io::error_code& error)
{
if (!parent || error)
return;
try {
parent->clock_tick();
}
catch (...)
{
}
schedule();
});
}
private:
AsioTimer timer;
OpenVPNClient* parent;
const Time::Duration period;
};
namespace Private { namespace Private {
class ClientState class ClientState
{ {
@@ -355,6 +402,7 @@ namespace openvpn {
MySessionStats::Ptr stats; MySessionStats::Ptr stats;
MyClientEvents::Ptr events; MyClientEvents::Ptr events;
ClientConnect::Ptr session; ClientConnect::Ptr session;
std::unique_ptr<MyClockTick> clock_tick;
// extra settings submitted by API client // extra settings submitted by API client
std::string server_override; std::string server_override;
@@ -376,6 +424,7 @@ namespace openvpn {
ProtoContextOptions::Ptr proto_context_options; ProtoContextOptions::Ptr proto_context_options;
PeerInfo::Set::Ptr extra_peer_info; PeerInfo::Set::Ptr extra_peer_info;
HTTPProxyTransport::Options::Ptr http_proxy_options; HTTPProxyTransport::Options::Ptr http_proxy_options;
unsigned int clock_tick_ms = 0;
#ifdef OPENVPN_GREMLIN #ifdef OPENVPN_GREMLIN
Gremlin::Config::Ptr gremlin_config; Gremlin::Config::Ptr gremlin_config;
#endif #endif
@@ -429,6 +478,8 @@ namespace openvpn {
socket_protect.detach_from_parent(); socket_protect.detach_from_parent();
reconnect_notify.detach_from_parent(); reconnect_notify.detach_from_parent();
remote_override.detach_from_parent(); remote_override.detach_from_parent();
if (clock_tick)
clock_tick->detach_from_parent();
if (stats) if (stats)
stats->detach_from_parent(); stats->detach_from_parent();
if (events) if (events)
@@ -474,6 +525,13 @@ namespace openvpn {
async_stop_local_.stop(); async_stop_local_.stop();
} }
// disconnect
void on_disconnect()
{
if (clock_tick)
clock_tick->cancel();
}
private: private:
ClientState(const ClientState&) = delete; ClientState(const ClientState&) = delete;
ClientState& operator=(const ClientState&) = delete; ClientState& operator=(const ClientState&) = delete;
@@ -596,6 +654,7 @@ namespace openvpn {
state->dco = config.dco; state->dco = config.dco;
state->echo = config.echo; state->echo = config.echo;
state->info = config.info; state->info = config.info;
state->clock_tick_ms = config.clockTickMS;
if (!config.gremlinConfig.empty()) if (!config.gremlinConfig.empty())
{ {
#ifdef OPENVPN_GREMLIN #ifdef OPENVPN_GREMLIN
@@ -755,7 +814,9 @@ namespace openvpn {
OPENVPN_CLIENT_EXPORT Status OpenVPNClient::connect() OPENVPN_CLIENT_EXPORT Status OpenVPNClient::connect()
{ {
#if !defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
openvpn_io::detail::signal_blocker signal_blocker; // signals should be handled by parent thread openvpn_io::detail::signal_blocker signal_blocker; // signals should be handled by parent thread
#endif
#if defined(OPENVPN_LOG_LOGTHREAD_H) && !defined(OPENVPN_LOG_LOGBASE_H) #if defined(OPENVPN_LOG_LOGTHREAD_H) && !defined(OPENVPN_LOG_LOGBASE_H)
#ifdef OPENVPN_LOG_GLOBAL #ifdef OPENVPN_LOG_GLOBAL
#error ovpn3 core logging object only supports thread-local scope #error ovpn3 core logging object only supports thread-local scope
@@ -815,6 +876,9 @@ namespace openvpn {
cc.socket_protect = &state->socket_protect; cc.socket_protect = &state->socket_protect;
cc.builder = this; cc.builder = this;
#endif #endif
#if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
cc.extern_tun_factory = this;
#endif
// force Session ID use and disable password cache if static challenge is enabled // force Session ID use and disable password cache if static challenge is enabled
if (state->creds if (state->creds
@@ -864,6 +928,13 @@ namespace openvpn {
// instantiate top-level client session // instantiate top-level client session
state->session.reset(new ClientConnect(*state->io_context(), client_options)); state->session.reset(new ClientConnect(*state->io_context(), client_options));
// convenience clock tick
if (state->clock_tick_ms)
{
state->clock_tick.reset(new MyClockTick(*state->io_context(), this, state->clock_tick_ms));
state->clock_tick->schedule();
}
// raise an exception if app has expired // raise an exception if app has expired
check_app_expired(); check_app_expired();
@@ -1142,7 +1213,7 @@ namespace openvpn {
{ {
ClientConnect* session = state->session.get(); ClientConnect* session = state->session.get();
if (session) if (session)
state->session->thread_safe_resume(); session->thread_safe_resume();
} }
} }
@@ -1152,7 +1223,7 @@ namespace openvpn {
{ {
ClientConnect* session = state->session.get(); ClientConnect* session = state->session.get();
if (session) if (session)
state->session->thread_safe_reconnect(seconds); session->thread_safe_reconnect(seconds);
} }
} }
@@ -1162,10 +1233,19 @@ namespace openvpn {
{ {
ClientConnect* session = state->session.get(); ClientConnect* session = state->session.get();
if (session) if (session)
state->session->thread_safe_post_cc_msg(msg); session->thread_safe_post_cc_msg(msg);
} }
} }
OPENVPN_CLIENT_EXPORT void OpenVPNClient::clock_tick()
{
}
OPENVPN_CLIENT_EXPORT void OpenVPNClient::on_disconnect()
{
state->on_disconnect();
}
OPENVPN_CLIENT_EXPORT std::string OpenVPNClient::crypto_self_test() OPENVPN_CLIENT_EXPORT std::string OpenVPNClient::crypto_self_test()
{ {
return SelfTest::crypto_self_test(); return SelfTest::crypto_self_test();
@@ -29,6 +29,7 @@
#include <utility> #include <utility>
#include <openvpn/tun/builder/base.hpp> #include <openvpn/tun/builder/base.hpp>
#include <openvpn/tun/extern/fw.hpp>
#include <openvpn/pki/epkibase.hpp> #include <openvpn/pki/epkibase.hpp>
namespace openvpn { namespace openvpn {
@@ -269,6 +270,11 @@ namespace openvpn {
// pass through control channel INFO notifications via "INFO" event // pass through control channel INFO notifications via "INFO" event
bool info = false; bool info = false;
// Periodic convenience clock tick in milliseconds.
// Will call clock_tick() at a frequency defined by this parameter.
// Set to 0 to disable.
unsigned int clockTickMS = 0;
// Gremlin configuration (requires that the core is built with OPENVPN_GREMLIN) // Gremlin configuration (requires that the core is built with OPENVPN_GREMLIN)
std::string gremlinConfig; std::string gremlinConfig;
}; };
@@ -403,7 +409,11 @@ namespace openvpn {
}; };
// Top-level OpenVPN client class. // Top-level OpenVPN client class.
class OpenVPNClient : public TunBuilderBase, public LogReceiver, private ExternalPKIBase { class OpenVPNClient : public TunBuilderBase, // expose tun builder virtual methods
public LogReceiver, // log message notification
public ExternalTun::Factory, // low-level tun override
private ExternalPKIBase
{
public: public:
OpenVPNClient(); OpenVPNClient();
virtual ~OpenVPNClient(); virtual ~OpenVPNClient();
@@ -523,6 +533,9 @@ namespace openvpn {
virtual bool remote_override_enabled(); virtual bool remote_override_enabled();
virtual void remote_override(RemoteOverride&); virtual void remote_override(RemoteOverride&);
// Periodic convenience clock tick, controlled by Config::clock_tick_ms
virtual void clock_tick();
// Do a crypto library self test // Do a crypto library self test
static std::string crypto_self_test(); static std::string crypto_self_test();
@@ -561,6 +574,9 @@ namespace openvpn {
void check_app_expired(); void check_app_expired();
static MergeConfig build_merge_config(const ProfileMerge&); static MergeConfig build_merge_config(const ProfileMerge&);
friend class MyClientEvents;
void on_disconnect();
// from ExternalPKIBase // from ExternalPKIBase
virtual bool sign(const std::string& data, std::string& sig); virtual bool sign(const std::string& data, std::string& sig);
@@ -12,8 +12,9 @@
#include "ovpncli.hpp" #include "ovpncli.hpp"
%} %}
// ignore ClientAPI::OpenVPNClient bases other than TunBuilderBase // ignore these ClientAPI::OpenVPNClient bases
%ignore openvpn::ClientAPI::LogReceiver; %ignore openvpn::ClientAPI::LogReceiver;
%ignore openvpn::ExternalTun::Factory;
// modify exported C++ class names to incorporate their enclosing namespace // modify exported C++ class names to incorporate their enclosing namespace
%rename(ClientAPI_OpenVPNClient) OpenVPNClient; %rename(ClientAPI_OpenVPNClient) OpenVPNClient;
@@ -47,4 +48,5 @@ namespace std {
// interface to be bridged between C++ and target language // interface to be bridged between C++ and target language
%include "openvpn/pki/epkibase.hpp" %include "openvpn/pki/epkibase.hpp"
%include "openvpn/tun/builder/base.hpp" %include "openvpn/tun/builder/base.hpp"
%import "openvpn/tun/extern/fw.hpp" // ignored
%include "ovpncli.hpp" %include "ovpncli.hpp"
@@ -26,13 +26,11 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
#include <utility>
#include <exception>
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/buffer/buffer.hpp>
// Wrapper classes for Apple Core Foundation objects. // Wrapper classes for Apple Core Foundation objects.
#define OPENVPN_CF_WRAP(cls, castmeth, cftype, idmeth) \ #define OPENVPN_CF_WRAP(cls, castmeth, cftype, idmeth) \
@@ -52,7 +50,7 @@
{ \ { \
CFTypeRef o = Type<cftype>::cast(obj); \ CFTypeRef o = Type<cftype>::cast(obj); \
if (o) \ if (o) \
return cls(cftype(o), BORROW); \ return cls(cftype(o), GET); \
else \ else \
return cls(); \ return cls(); \
} }
@@ -60,9 +58,9 @@
namespace openvpn { namespace openvpn {
namespace CF namespace CF
{ {
enum Own { enum Rule {
OWN, CREATE, // create rule
BORROW GET // get rule
}; };
template <typename T> struct Type {}; template <typename T> struct Type {};
@@ -73,10 +71,9 @@ namespace openvpn {
public: public:
Wrap() : obj_(nullptr) {} Wrap() : obj_(nullptr) {}
// Set own=BORROW if we don't currently own the object explicit Wrap(T obj, const Rule rule=CREATE)
explicit Wrap(T obj, const Own own=OWN)
{ {
if (own == BORROW && obj) if (rule == GET && obj)
CFRetain(obj); CFRetain(obj);
obj_ = obj; obj_ = obj;
} }
@@ -118,9 +115,9 @@ namespace openvpn {
std::swap(obj_, other.obj_); std::swap(obj_, other.obj_);
} }
void reset(T obj=nullptr, const Own own=OWN) void reset(T obj=nullptr, const Rule rule=CREATE)
{ {
if (own == BORROW && obj) if (rule == GET && obj)
CFRetain(obj); CFRetain(obj);
if (obj_) if (obj_)
CFRelease(obj_); CFRelease(obj_);
@@ -129,15 +126,20 @@ namespace openvpn {
bool defined() const { return obj_ != nullptr; } bool defined() const { return obj_ != nullptr; }
explicit operator bool() const noexcept
{
return defined();
}
T operator()() const { return obj_; } T operator()() const { return obj_; }
CFTypeRef generic() const { return (CFTypeRef)obj_; } CFTypeRef generic() const { return (CFTypeRef)obj_; }
static T cast(CFTypeRef obj) { return T(Type<T>::cast(obj)); } static T cast(CFTypeRef obj) { return T(Type<T>::cast(obj)); }
static Wrap from_generic(CFTypeRef obj, const Own own=OWN) static Wrap from_generic(CFTypeRef obj, const Rule rule=CREATE)
{ {
return Wrap(cast(obj), own); return Wrap(cast(obj), rule);
} }
T release() T release()
@@ -155,7 +157,7 @@ namespace openvpn {
} }
// Intended for use with Core Foundation methods that require // Intended for use with Core Foundation methods that require
// a T* for saving a (non-borrowed) return value // a T* for saving a create-rule return value
T* mod_ref() T* mod_ref()
{ {
if (obj_) if (obj_)
@@ -181,14 +183,11 @@ namespace openvpn {
} }
private: private:
Wrap& operator=(T obj); // prevent use because no way to pass ownership parameter Wrap& operator=(T obj) = delete; // prevent use because no way to pass rule parameter
T obj_; T obj_;
}; };
// essentially a vector of void *, used as source for array and dictionary constructors
typedef BufferAllocatedType<CFTypeRef> SrcList;
// common CF types // common CF types
OPENVPN_CF_WRAP(String, string_cast, CFStringRef, CFStringGetTypeID) OPENVPN_CF_WRAP(String, string_cast, CFStringRef, CFStringGetTypeID)
@@ -207,7 +206,7 @@ namespace openvpn {
inline Generic generic_cast(CFTypeRef obj) inline Generic generic_cast(CFTypeRef obj)
{ {
return Generic(obj, BORROW); return Generic(obj, GET);
} }
// constructors // constructors
@@ -219,7 +218,7 @@ namespace openvpn {
inline String string(CFStringRef str) inline String string(CFStringRef str)
{ {
return String(str, BORROW); return String(str, GET);
} }
inline String string(const String& str) inline String string(const String& str)
@@ -267,11 +266,6 @@ namespace openvpn {
return Array(CFArrayCreate(kCFAllocatorDefault, values, numValues, &kCFTypeArrayCallBacks)); return Array(CFArrayCreate(kCFAllocatorDefault, values, numValues, &kCFTypeArrayCallBacks));
} }
inline Array array(const SrcList& values)
{
return array((const void **)values.c_data(), values.size());
}
inline Dict dict(const void **keys, const void **values, CFIndex numValues) inline Dict dict(const void **keys, const void **values, CFIndex numValues)
{ {
return Dict(CFDictionaryCreate(kCFAllocatorDefault, return Dict(CFDictionaryCreate(kCFAllocatorDefault,
@@ -282,19 +276,14 @@ namespace openvpn {
&kCFTypeDictionaryValueCallBacks)); &kCFTypeDictionaryValueCallBacks));
} }
inline Dict dict(const SrcList& keys, const SrcList& values)
{
return dict((const void **)keys.c_data(), (const void **)values.c_data(), std::min(keys.size(), values.size()));
}
inline Dict const_dict(MutableDict& mdict) inline Dict const_dict(MutableDict& mdict)
{ {
return Dict(mdict(), CF::BORROW); return Dict(mdict(), CF::GET);
} }
inline Array const_array(MutableArray& marray) inline Array const_array(MutableArray& marray)
{ {
return Array(marray(), CF::BORROW); return Array(marray(), CF::GET);
} }
inline Dict empty_dict() inline Dict empty_dict()
@@ -374,7 +363,13 @@ namespace openvpn {
// string methods // string methods
OPENVPN_SIMPLE_EXCEPTION(cppstring_error); struct cppstring_error : public std::exception
{
virtual const char* what() const throw()
{
return "cppstring_error";
}
};
inline std::string cppstring(CFStringRef str) inline std::string cppstring(CFStringRef str)
{ {
@@ -22,7 +22,8 @@
#ifndef OPENVPN_APPLECRYPTO_CF_CFHELPER_H #ifndef OPENVPN_APPLECRYPTO_CF_CFHELPER_H
#define OPENVPN_APPLECRYPTO_CF_CFHELPER_H #define OPENVPN_APPLECRYPTO_CF_CFHELPER_H
#include <openvpn/applecrypto/cf/cf.hpp> #include <openvpn/buffer/buffer.hpp>
#include <openvpn/apple/cf/cf.hpp>
// These methods build on the Wrapper classes for Apple Core Foundation objects // These methods build on the Wrapper classes for Apple Core Foundation objects
// defined in cf.hpp. They add additional convenience methods, such as dictionary // defined in cf.hpp. They add additional convenience methods, such as dictionary
@@ -31,6 +32,19 @@
namespace openvpn { namespace openvpn {
namespace CF { namespace CF {
// essentially a vector of void *, used as source for array and dictionary constructors
typedef BufferAllocatedType<CFTypeRef> SrcList;
inline Array array(const SrcList& values)
{
return array((const void **)values.c_data(), values.size());
}
inline Dict dict(const SrcList& keys, const SrcList& values)
{
return dict((const void **)keys.c_data(), (const void **)values.c_data(), std::min(keys.size(), values.size()));
}
inline CFTypeRef mutable_dict_new() inline CFTypeRef mutable_dict_new()
{ {
return CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); return CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
@@ -0,0 +1,33 @@
// 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-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFHOST_H
#define OPENVPN_APPLECRYPTO_CF_CFHOST_H
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Host, host_cast, CFHostRef, CFHostGetTypeID)
}
}
#endif
@@ -19,14 +19,14 @@
// along with this program in the COPYING file. // along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>. // If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLE_RUNLOOP_H #ifndef OPENVPN_APPLECRYPTO_CF_CFRUNLOOP_H
#define OPENVPN_APPLE_RUNLOOP_H #define OPENVPN_APPLECRYPTO_CF_CFRUNLOOP_H
#include <openvpn/applecrypto/cf/cf.hpp> #include <openvpn/apple/cf/cf.hpp>
namespace openvpn { namespace openvpn {
namespace CF { namespace CF {
OPENVPN_CF_WRAP(RunLoop, runloop_cast, CFRunLoopRef, CFRunLoopGetTypeID); OPENVPN_CF_WRAP(RunLoop, runloop_cast, CFRunLoopRef, CFRunLoopGetTypeID)
OPENVPN_CF_WRAP(RunLoopSource, runloop_source_cast, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID); OPENVPN_CF_WRAP(RunLoopSource, runloop_source_cast, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID);
} }
} }
@@ -36,7 +36,7 @@
#include <openvpn/common/size.hpp> #include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp> #include <openvpn/common/exception.hpp>
#include <openvpn/applecrypto/cf/cf.hpp> #include <openvpn/apple/cf/cf.hpp>
// Define C++ wrappings for Apple security-related objects. // Define C++ wrappings for Apple security-related objects.
@@ -0,0 +1,33 @@
// 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-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#define OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Socket, socket_cast, CFSocketRef, CFSocketGetTypeID)
}
}
#endif
@@ -0,0 +1,34 @@
// 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-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#define OPENVPN_APPLECRYPTO_CF_CFSTREAM_H
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(ReadStream, read_stream_cast, CFReadStreamRef, CFReadStreamGetTypeID)
OPENVPN_CF_WRAP(WriteStream, write_stream_cast, CFWriteStreamRef, CFWriteStreamGetTypeID)
}
}
#endif
@@ -22,7 +22,7 @@
#ifndef OPENVPN_APPLECRYPTO_CF_CFTIMER_H #ifndef OPENVPN_APPLECRYPTO_CF_CFTIMER_H
#define OPENVPN_APPLECRYPTO_CF_CFTIMER_H #define OPENVPN_APPLECRYPTO_CF_CFTIMER_H
#include <openvpn/applecrypto/cf/cf.hpp> #include <openvpn/apple/cf/cf.hpp>
namespace openvpn { namespace openvpn {
namespace CF { namespace CF {
@@ -21,7 +21,7 @@
#include <string> #include <string>
#include <openvpn/applecrypto/util/reach.hpp> #include <openvpn/apple/reach.hpp>
#include <openvpn/netconf/enumiface.hpp> #include <openvpn/netconf/enumiface.hpp>
#ifndef OPENVPN_APPLECRYPTO_UTIL_IOSACTIVEIFACE_H #ifndef OPENVPN_APPLECRYPTO_UTIL_IOSACTIVEIFACE_H
@@ -28,11 +28,11 @@
#include <thread> #include <thread>
#include <openvpn/log/logthread.hpp> #include <openvpn/log/logthread.hpp>
#include <openvpn/applecrypto/cf/cftimer.hpp> #include <openvpn/apple/cf/cftimer.hpp>
#include <openvpn/applecrypto/cf/cfhelper.hpp> #include <openvpn/apple/cf/cfhelper.hpp>
#include <openvpn/applecrypto/util/reachable.hpp> #include <openvpn/apple/cf/cfrunloop.hpp>
#include <openvpn/apple/reachable.hpp>
#include <openvpn/client/clilife.hpp> #include <openvpn/client/clilife.hpp>
#include <openvpn/apple/runloop.hpp>
#include <openvpn/apple/macsleep.hpp> #include <openvpn/apple/macsleep.hpp>
#include <openvpn/apple/scdynstore.hpp> #include <openvpn/apple/scdynstore.hpp>
@@ -126,7 +126,7 @@ namespace openvpn {
void thread_func() void thread_func()
{ {
runloop.reset(CFRunLoopGetCurrent(), CF::BORROW); runloop.reset(CFRunLoopGetCurrent(), CF::GET);
Log::Context logctx(logwrap); Log::Context logctx(logwrap);
try { try {
// set up dynamic store query object // set up dynamic store query object
@@ -77,8 +77,8 @@
#include <memory> #include <memory>
#include <openvpn/common/socktypes.hpp> #include <openvpn/common/socktypes.hpp>
#include <openvpn/applecrypto/cf/cf.hpp> #include <openvpn/apple/cf/cf.hpp>
#include <openvpn/applecrypto/util/reach.hpp> #include <openvpn/apple/reach.hpp>
namespace openvpn { namespace openvpn {
namespace CF { namespace CF {
@@ -24,7 +24,7 @@
#include <SystemConfiguration/SCDynamicStore.h> #include <SystemConfiguration/SCDynamicStore.h>
#include <openvpn/applecrypto/cf/cf.hpp> #include <openvpn/apple/cf/cf.hpp>
namespace openvpn { namespace openvpn {
namespace CF { namespace CF {
@@ -36,7 +36,7 @@
#include <openvpn/common/string.hpp> #include <openvpn/common/string.hpp>
#include <openvpn/crypto/static_key.hpp> #include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/cryptoalgs.hpp> #include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/applecrypto/cf/error.hpp> #include <openvpn/apple/cf/error.hpp>
namespace openvpn { namespace openvpn {
namespace AppleCrypto { namespace AppleCrypto {
@@ -34,7 +34,7 @@
#include <openvpn/common/exception.hpp> #include <openvpn/common/exception.hpp>
#include <openvpn/common/string.hpp> #include <openvpn/common/string.hpp>
#include <openvpn/crypto/cryptoalgs.hpp> #include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/applecrypto/cf/error.hpp> #include <openvpn/apple/cf/error.hpp>
#define OPENVPN_DIGEST_CONTEXT(TYPE) CC_##TYPE##_CTX TYPE##_ctx #define OPENVPN_DIGEST_CONTEXT(TYPE) CC_##TYPE##_CTX TYPE##_ctx
@@ -47,8 +47,8 @@
#include <openvpn/frame/frame.hpp> #include <openvpn/frame/frame.hpp>
#include <openvpn/frame/memq_stream.hpp> #include <openvpn/frame/memq_stream.hpp>
#include <openvpn/pki/epkibase.hpp> #include <openvpn/pki/epkibase.hpp>
#include <openvpn/applecrypto/cf/cfsec.hpp> #include <openvpn/apple/cf/cfsec.hpp>
#include <openvpn/applecrypto/cf/error.hpp> #include <openvpn/apple/cf/error.hpp>
#include <openvpn/ssl/tlsver.hpp> #include <openvpn/ssl/tlsver.hpp>
#include <openvpn/ssl/sslconsts.hpp> #include <openvpn/ssl/sslconsts.hpp>
#include <openvpn/ssl/sslapi.hpp> #include <openvpn/ssl/sslapi.hpp>
@@ -139,7 +139,9 @@ namespace openvpn {
#if !defined(OPENVPN_PLATFORM_WIN) #if !defined(OPENVPN_PLATFORM_WIN)
virtual void set_cloexec() override virtual void set_cloexec() override
{ {
SockOpt::set_cloexec(socket.native_handle()); const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
} }
#endif #endif
@@ -207,7 +209,9 @@ namespace openvpn {
virtual void set_cloexec() override virtual void set_cloexec() override
{ {
SockOpt::set_cloexec(socket.native_handle()); const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
} }
virtual void close() override virtual void close() override
@@ -156,6 +156,9 @@ namespace openvpn {
ClientEvent::Base::Ptr ev = new ClientEvent::Disconnected(); ClientEvent::Base::Ptr ev = new ClientEvent::Disconnected();
client_options->events().add_event(std::move(ev)); client_options->events().add_event(std::move(ev));
#ifdef OPENVPN_IO_REQUIRES_STOP
io_context.stop();
#endif
} }
} }
@@ -214,7 +217,7 @@ namespace openvpn {
OPENVPN_LOG("Client terminated, reconnecting in " << seconds << "..."); OPENVPN_LOG("Client terminated, reconnecting in " << seconds << "...");
server_poll_timer.cancel(); server_poll_timer.cancel();
client_options->remote_reset_cache_item(); client_options->remote_reset_cache_item();
restart_wait_timer.expires_at(Time::now() + Time::Duration::seconds(seconds)); restart_wait_timer.expires_after(Time::Duration::seconds(seconds));
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error) restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{ {
self->restart_wait_callback(gen, error); self->restart_wait_callback(gen, error);
@@ -345,7 +348,7 @@ namespace openvpn {
{ {
if (!conn_timer_pending && conn_timeout > 0) if (!conn_timer_pending && conn_timeout > 0)
{ {
conn_timer.expires_at(Time::now() + Time::Duration::seconds(conn_timeout)); conn_timer.expires_after(Time::Duration::seconds(conn_timeout));
conn_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error) conn_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{ {
self->conn_timer_callback(gen, error); self->conn_timer_callback(gen, error);
@@ -393,7 +396,7 @@ namespace openvpn {
server_poll_timer.cancel(); server_poll_timer.cancel();
interim_finalize(); interim_finalize();
client_options->remote_reset_cache_item(); client_options->remote_reset_cache_item();
restart_wait_timer.expires_at(Time::now() + Time::Duration::milliseconds(delay_ms)); restart_wait_timer.expires_after(Time::Duration::milliseconds(delay_ms));
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error) restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{ {
self->restart_wait_callback(gen, error); self->restart_wait_callback(gen, error);
@@ -589,7 +592,7 @@ namespace openvpn {
restart_wait_timer.cancel(); restart_wait_timer.cancel();
if (client_options->server_poll_timeout_enabled()) if (client_options->server_poll_timeout_enabled())
{ {
server_poll_timer.expires_at(Time::now() + client_options->server_poll_timeout()); server_poll_timer.expires_after(client_options->server_poll_timeout());
server_poll_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error) server_poll_timer.async_wait([self=Ptr(this), gen=generation](const openvpn_io::error_code& error)
{ {
self->server_poll_callback(gen, error); self->server_poll_callback(gen, error);
@@ -66,8 +66,9 @@
#include <openvpn/client/cliemuexr.hpp> #include <openvpn/client/cliemuexr.hpp>
#endif #endif
#if defined(OPENVPN_CUSTOM_TUN_FACTORY) #if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
// includer of this file must define OPENVPN_CUSTOM_TUN_FACTORY class // requires that client implements ExternalTun::Factory::new_tun_factory
#include <openvpn/tun/extern/config.hpp>
#elif defined(USE_TUN_BUILDER) #elif defined(USE_TUN_BUILDER)
#include <openvpn/tun/builder/client.hpp> #include <openvpn/tun/builder/client.hpp>
#elif defined(OPENVPN_PLATFORM_LINUX) && !defined(OPENVPN_FORCE_TUN_NULL) #elif defined(OPENVPN_PLATFORM_LINUX) && !defined(OPENVPN_FORCE_TUN_NULL)
@@ -151,6 +152,10 @@ namespace openvpn {
#if defined(USE_TUN_BUILDER) #if defined(USE_TUN_BUILDER)
TunBuilderBase* builder = nullptr; TunBuilderBase* builder = nullptr;
#endif #endif
#if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
ExternalTun::Factory* extern_tun_factory = nullptr;
#endif
}; };
ClientOptions(const OptionList& opt, // only needs to remain in scope for duration of constructor call ClientOptions(const OptionList& opt, // only needs to remain in scope for duration of constructor call
@@ -191,7 +196,7 @@ namespace openvpn {
rng.reset(new SSLLib::RandomAPI(false)); rng.reset(new SSLLib::RandomAPI(false));
prng.reset(new SSLLib::RandomAPI(true)); prng.reset(new SSLLib::RandomAPI(true));
#if defined(ENABLE_DCO) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_CUSTOM_TUN_FACTORY) #if defined(ENABLE_DCO) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_EXTERNAL_TUN_FACTORY)
if (config.dco) if (config.dco)
dco = DCOTransport::new_controller(); dco = DCOTransport::new_controller();
#else #else
@@ -292,17 +297,22 @@ namespace openvpn {
} }
else else
{ {
#if defined(OPENVPN_CUSTOM_TUN_FACTORY) #if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
{ {
OPENVPN_CUSTOM_TUN_FACTORY::Ptr tunconf = OPENVPN_CUSTOM_TUN_FACTORY::new_obj(); ExternalTun::Config tunconf;
tunconf->tun_prop.session_name = session_name; tunconf.tun_prop.layer = layer;
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback; tunconf.tun_prop.session_name = session_name;
tunconf.tun_prop.google_dns_fallback = config.google_dns_fallback;
if (tun_mtu) if (tun_mtu)
tunconf->tun_prop.mtu = tun_mtu; tunconf.tun_prop.mtu = tun_mtu;
tunconf->frame = frame; tunconf.frame = frame;
tunconf->stats = cli_stats; tunconf.stats = cli_stats;
tunconf->tun_prop.remote_list = remote_list; tunconf.tun_prop.remote_list = remote_list;
tun_factory = tunconf; tunconf.tun_persist = config.tun_persist;
tunconf.stop = config.stop;
tun_factory.reset(config.extern_tun_factory->new_tun_factory(tunconf, opt));
if (!tun_factory)
throw option_error("OPENVPN_EXTERNAL_TUN_FACTORY: no tun factory");
} }
#elif defined(USE_TUN_BUILDER) #elif defined(USE_TUN_BUILDER)
{ {
@@ -448,7 +458,7 @@ namespace openvpn {
// IPv6 // IPv6
{ {
const unsigned int n = push_base->singleton.extend(opt, "block-ipv6"); const unsigned int n = push_base->singleton.extend(opt, "block-ipv6");
if (!n && config.ipv6() == IPv6Setting::NO) if (!n && config.ipv6() == IPv6Setting::No)
push_base->singleton.emplace_back("block-ipv6"); push_base->singleton.emplace_back("block-ipv6");
} }
} }
@@ -463,9 +473,9 @@ namespace openvpn {
PeerInfo::Set::Ptr pi(new PeerInfo::Set); PeerInfo::Set::Ptr pi(new PeerInfo::Set);
// IPv6 // IPv6
if (config.ipv6() == IPv6Setting::NO) if (config.ipv6() == IPv6Setting::No)
pi->emplace_back("IV_IPv6", "0"); pi->emplace_back("IV_IPv6", "0");
else if (config.ipv6() == IPv6Setting::YES) else if (config.ipv6() == IPv6Setting::Yes)
pi->emplace_back("IV_IPv6", "1"); pi->emplace_back("IV_IPv6", "1");
// autologin sessions // autologin sessions
@@ -777,7 +777,7 @@ namespace openvpn {
{ {
if (!received_options.partial()) if (!received_options.partial())
{ {
push_request_timer.expires_at(now() + dur); push_request_timer.expires_after(dur);
push_request_timer.async_wait([self=Ptr(this), dur](const openvpn_io::error_code& error) push_request_timer.async_wait([self=Ptr(this), dur](const openvpn_io::error_code& error)
{ {
self->send_push_request_callback(dur, error); self->send_push_request_callback(dur, error);
@@ -865,7 +865,7 @@ namespace openvpn {
void schedule_inactive_timer() void schedule_inactive_timer()
{ {
inactive_timer.expires_at(now() + inactive_duration); inactive_timer.expires_after(inactive_duration);
inactive_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error) inactive_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{ {
self->inactive_callback(error); self->inactive_callback(error);
@@ -955,7 +955,7 @@ namespace openvpn {
void schedule_info_hold_callback() void schedule_info_hold_callback()
{ {
Base::update_now(); Base::update_now();
info_hold_timer.expires_at(now() + Time::Duration::seconds(1)); info_hold_timer.expires_after(Time::Duration::seconds(1));
info_hold_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error) info_hold_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{ {
self->info_hold_callback(error); self->info_hold_callback(error);
@@ -24,36 +24,72 @@
#ifndef OPENVPN_COMMON_FUNCTION_H #ifndef OPENVPN_COMMON_FUNCTION_H
#define OPENVPN_COMMON_FUNCTION_H #define OPENVPN_COMMON_FUNCTION_H
#include <cstddef> // for std::size_t
#include <utility> // for std::move
#include <new> #include <new>
#include <utility>
namespace openvpn { namespace openvpn {
template <typename F> // F -- function type (usually a lambda expression)
// N (default=3) -- max size of functor in void* machine words before we overflow to dynamic allocation
// INTERN_ONLY (default=false) -- if true, throw a static assertion if functor cannot be stored internally
template <typename F, std::size_t N=3, bool INTERN_ONLY=false>
class Function; class Function;
template <typename R, typename ... A> template <typename R, typename ... A, std::size_t N, bool INTERN_ONLY>
class Function<R(A...)> class Function<R(A...), N, INTERN_ONLY>
{ {
public: public:
static constexpr size_t N = 3; // max size of functor in machine words Function() noexcept
{
methods = nullptr;
}
template <typename T> template <typename T>
Function(T&& functor) noexcept Function(T&& functor) noexcept
{ {
static_assert(sizeof(Intern<T>) <= sizeof(data), "Functor too large"); construct(std::move(functor));
setup_methods<T>();
new (data) Intern<T>(std::move(functor));
} }
Function(Function&& f) noexcept Function(Function&& other) noexcept
{ {
methods = f.methods; methods = other.methods;
methods->move(data, f.data); other.methods = nullptr;
if (methods)
methods->move(data, other.data);
}
Function& operator=(Function&& other) noexcept
{
if (methods)
methods->destruct(data);
methods = other.methods;
other.methods = nullptr;
if (methods)
methods->move(data, other.data);
return *this;
} }
~Function() ~Function()
{ {
methods->destruct(data); if (methods)
methods->destruct(data);
}
template <typename T>
void reset(T&& functor) noexcept
{
if (methods)
methods->destruct(data);
construct(std::move(functor));
}
void reset() noexcept
{
if (methods)
{
methods->destruct(data);
methods = nullptr;
}
} }
R operator()(A... args) R operator()(A... args)
@@ -61,7 +97,33 @@ namespace openvpn {
return methods->invoke(data, args...); return methods->invoke(data, args...);
} }
explicit operator bool() const noexcept
{
return methods != nullptr;
}
private: private:
template <typename T>
void construct(T&& functor) noexcept
{
constexpr bool is_intern = (sizeof(Intern<T>) <= sizeof(data));
static_assert(!INTERN_ONLY || is_intern, "Function: Intern<T> doesn't fit in data[] and INTERN_ONLY=true");
static_assert(sizeof(Extern<T>) <= sizeof(data), "Function: Extern<T> doesn't fit in data[]");
if (is_intern)
{
// store functor internally (in data)
setup_methods_intern<T>();
new (data) Intern<T>(std::move(functor));
}
else
{
// store functor externally (using new)
setup_methods_extern<T>();
new (data) Extern<T>(std::move(functor));
}
}
struct Methods struct Methods
{ {
R (*invoke)(void *, A...); R (*invoke)(void *, A...);
@@ -70,7 +132,7 @@ namespace openvpn {
}; };
template <typename T> template <typename T>
void setup_methods() void setup_methods_intern()
{ {
static const struct Methods m = { static const struct Methods m = {
&Intern<T>::invoke, &Intern<T>::invoke,
@@ -80,15 +142,22 @@ namespace openvpn {
methods = &m; methods = &m;
} }
template <typename T>
void setup_methods_extern()
{
static const struct Methods m = {
&Extern<T>::invoke,
&Extern<T>::move,
&Extern<T>::destruct,
};
methods = &m;
}
// store functor internally (in data)
template <typename T> template <typename T>
class Intern class Intern
{ {
public: public:
Intern(Intern&& obj) noexcept
: functor_(std::move(obj.functor_))
{
}
Intern(T&& functor) noexcept Intern(T&& functor) noexcept
: functor_(std::move(functor)) : functor_(std::move(functor))
{ {
@@ -116,6 +185,40 @@ namespace openvpn {
T functor_; T functor_;
}; };
// store functor externally (using new)
template <typename T>
class Extern
{
public:
Extern(T&& functor) noexcept
: functor_(new T(std::move(functor)))
{
}
static R invoke(void *ptr, A... args)
{
Extern* self = reinterpret_cast<Extern<T>*>(ptr);
return (*self->functor_)(args...);
}
static void move(void *dest, void *src)
{
Extern* d = reinterpret_cast<Extern<T>*>(dest);
Extern* s = reinterpret_cast<Extern<T>*>(src);
d->functor_ = s->functor_;
// no need to set s->functor_=nullptr because parent will not destruct src after move
}
static void destruct(void *ptr)
{
Extern* self = reinterpret_cast<Extern<T>*>(ptr);
delete self->functor_;
}
private:
T* functor_;
};
const Methods* methods; const Methods* methods;
void* data[N]; void* data[N];
}; };
@@ -152,12 +152,18 @@ namespace openvpn {
{ {
if (!argv.empty()) if (!argv.empty())
{ {
RedirectPipe::InOut inout;
os << to_string() << std::endl; os << to_string() << std::endl;
#ifdef OPENVPN_PROCESS_AVOID_PIPES
const int status = system_cmd(argv[0], argv);
if (status < 0)
os << "Error: command failed to execute" << std::endl;
#else
RedirectPipe::InOut inout;
const int status = system_cmd(argv[0], argv, nullptr, inout, true); const int status = system_cmd(argv[0], argv, nullptr, inout, true);
if (status < 0) if (status < 0)
os << "Error: command failed to execute" << std::endl; os << "Error: command failed to execute" << std::endl;
os << inout.out; os << inout.out;
#endif
} }
else else
os << "Error: command called with empty argv" << std::endl; os << "Error: command called with empty argv" << std::endl;
@@ -123,7 +123,7 @@ namespace openvpn {
signal_rearm(); signal_rearm();
#ifdef OPENVPN_EXIT_IN #ifdef OPENVPN_EXIT_IN
exit_timer.expires_at(Time::now() + Time::Duration::seconds(OPENVPN_EXIT_IN)); exit_timer.expires_after(Time::Duration::seconds(OPENVPN_EXIT_IN));
exit_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error) exit_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{ {
if (!error) if (!error)
@@ -54,6 +54,11 @@ namespace openvpn {
{ {
return time_point(duration(t.raw())); return time_point(duration(t.raw()));
} }
static duration to_duration(const Time::Duration& d)
{
return duration(d.raw());
}
}; };
class AsioTimer : public openvpn_io::basic_waitable_timer<AsioClock> class AsioTimer : public openvpn_io::basic_waitable_timer<AsioClock>
@@ -68,6 +73,11 @@ namespace openvpn {
{ {
return openvpn_io::basic_waitable_timer<AsioClock>::expires_at(AsioClock::to_time_point(t)); return openvpn_io::basic_waitable_timer<AsioClock>::expires_at(AsioClock::to_time_point(t));
} }
std::size_t expires_after(const Time::Duration& d)
{
return openvpn_io::basic_waitable_timer<AsioClock>::expires_after(AsioClock::to_duration(d));
}
}; };
} }
@@ -0,0 +1,45 @@
// 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-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_TUN_EXTERN_CONFIG_H
#define OPENVPN_TUN_EXTERN_CONFIG_H
// These includes are also intended to resolve forward references in fw.hpp
#include <openvpn/common/options.hpp>
#include <openvpn/tun/client/tunbase.hpp>
#include <openvpn/tun/client/tunprop.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/common/stop.hpp>
namespace openvpn {
namespace ExternalTun {
struct Config
{
TunProp::Config tun_prop;
Frame::Ptr frame;
SessionStats::Ptr stats;
Stop* stop = nullptr;
bool tun_persist = false;
};
}
}
#endif
@@ -0,0 +1,49 @@
// 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-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_TUN_EXTERN_FW_H
#define OPENVPN_TUN_EXTERN_FW_H
namespace openvpn {
#if defined(OPENVPN_EXTERNAL_TUN_FACTORY)
struct TunClientFactory;
class OptionList;
namespace ExternalTun {
struct Config; // defined in config.hpp
struct Factory
{
virtual TunClientFactory* new_tun_factory(const Config& conf, const OptionList& opt) = 0;
virtual ~Factory() {}
};
}
#else
namespace ExternalTun {
struct Factory {};
}
#endif
}
#endif
@@ -29,9 +29,9 @@ namespace openvpn {
{ {
public: public:
enum Type { enum Type {
NO, No,
YES, Yes,
DEFAULT, Default,
}; };
IPv6Setting() IPv6Setting()
@@ -49,11 +49,11 @@ namespace openvpn {
{ {
switch (type_) switch (type_)
{ {
case NO: case No:
return "no"; return "no";
case YES: case Yes:
return "yes"; return "yes";
case DEFAULT: case Default:
default: default:
return "default"; return "default";
} }
@@ -62,11 +62,11 @@ namespace openvpn {
static IPv6Setting parse(const std::string& str) static IPv6Setting parse(const std::string& str)
{ {
if (str == "no") if (str == "no")
return IPv6Setting(NO); return IPv6Setting(No);
else if (str == "yes") else if (str == "yes")
return IPv6Setting(YES); return IPv6Setting(Yes);
else if (str == "default") else if (str == "default")
return IPv6Setting(DEFAULT); return IPv6Setting(Default);
else else
throw Exception("IPv6Setting: unrecognized setting: '" + str + '\''); throw Exception("IPv6Setting: unrecognized setting: '" + str + '\'');
} }
@@ -82,7 +82,7 @@ namespace openvpn {
} }
private: private:
Type type_ = DEFAULT; Type type_ = Default;
}; };
} }
@@ -26,8 +26,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <net/route.h> #include <net/route.h>
#include <net/if.h>
#include <net/if_dl.h> #include <net/if_dl.h>
#include <cstring> #include <cstring>
@@ -34,7 +34,7 @@
#include <openvpn/common/process.hpp> #include <openvpn/common/process.hpp>
#include <openvpn/apple/macver.hpp> #include <openvpn/apple/macver.hpp>
#include <openvpn/apple/scdynstore.hpp> #include <openvpn/apple/scdynstore.hpp>
#include <openvpn/applecrypto/cf/cfhelper.hpp> #include <openvpn/apple/cf/cfhelper.hpp>
#include <openvpn/tun/builder/capture.hpp> #include <openvpn/tun/builder/capture.hpp>
namespace openvpn { namespace openvpn {
@@ -29,8 +29,8 @@
#include <openvpn/log/logthread.hpp> #include <openvpn/log/logthread.hpp>
#include <openvpn/common/action.hpp> #include <openvpn/common/action.hpp>
#include <openvpn/applecrypto/cf/cftimer.hpp> #include <openvpn/apple/cf/cftimer.hpp>
#include <openvpn/apple/runloop.hpp> #include <openvpn/apple/cf/cfrunloop.hpp>
#include <openvpn/tun/mac/macdns.hpp> #include <openvpn/tun/mac/macdns.hpp>
namespace openvpn { namespace openvpn {
@@ -183,7 +183,7 @@ namespace openvpn {
// as well. // as well.
void thread_func() void thread_func()
{ {
runloop.reset(CFRunLoopGetCurrent(), CF::BORROW); runloop.reset(CFRunLoopGetCurrent(), CF::GET);
Log::Context logctx(logwrap); Log::Context logctx(logwrap);
try { try {
@@ -28,7 +28,7 @@
#include <openvpn/common/exception.hpp> #include <openvpn/common/exception.hpp>
#include <openvpn/addr/ip.hpp> #include <openvpn/addr/ip.hpp>
#include <openvpn/apple/scdynstore.hpp> #include <openvpn/apple/scdynstore.hpp>
#include <openvpn/applecrypto/cf/cfhelper.hpp> #include <openvpn/apple/cf/cfhelper.hpp>
namespace openvpn { namespace openvpn {
struct MacGWInfo struct MacGWInfo
@@ -22,6 +22,8 @@
#ifndef OPENVPN_TUN_PERSIST_TUNWRAPASIO_H #ifndef OPENVPN_TUN_PERSIST_TUNWRAPASIO_H
#define OPENVPN_TUN_PERSIST_TUNWRAPASIO_H #define OPENVPN_TUN_PERSIST_TUNWRAPASIO_H
#include <utility>
namespace openvpn { namespace openvpn {
// This object supports that subset of the Asio stream // This object supports that subset of the Asio stream
@@ -48,17 +50,14 @@ namespace openvpn {
// subset of methods used by TunIO). // subset of methods used by TunIO).
// Prototypes from asio/windows/basic_stream_handle.hpp // Prototypes from asio/windows/basic_stream_handle.hpp
template <typename MutableBufferSequence, typename ReadHandler> template <typename MUTABLE_BUFFER, typename HANDLER>
ASIO_INITFN_RESULT_TYPE(ReadHandler, void async_read_some(const MUTABLE_BUFFER& buffers, HANDLER&& handler)
void (openvpn_io::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers,
ASIO_MOVE_ARG(ReadHandler) handler)
{ {
return tun_wrap->obj()->async_read_some(buffers, handler); return tun_wrap->obj()->async_read_some(buffers, std::move(handler));
} }
template <typename ConstBufferSequence> template <typename CONST_BUFFER>
std::size_t write_some(const ConstBufferSequence& buffers) std::size_t write_some(const CONST_BUFFER& buffers)
{ {
return tun_wrap->obj()->write_some(buffers); return tun_wrap->obj()->write_some(buffers);
} }
@@ -388,7 +388,7 @@ namespace openvpn {
void layer_2_schedule_timer(const unsigned int seconds) void layer_2_schedule_timer(const unsigned int seconds)
{ {
l2_timer.expires_at(Time::now() + Time::Duration::seconds(seconds)); l2_timer.expires_after(Time::Duration::seconds(seconds));
l2_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error) l2_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{ {
if (!error && !self->halt) if (!error && !self->halt)
@@ -292,13 +292,9 @@ fi
# Construct command # Construct command
if [ "$OBJC" == "1" ]; then if [ "$OBJC" == "1" ]; then
FLAGS="$FLAGS -fobjc-arc" FLAGS="$FLAGS -fobjc-arc"
CPPFLAGS="" LIBS="-framework Foundation $LIBS"
CPPFLAGS="$CPPFLAGS -I$O3/cliobjc" SRC="$1.mm"
CPPFLAGS="$CPPFLAGS -I$O3/clilib/dist-static-$PLATFORM/include" CMD="$GPP_CMD $FLAGS $GCC_EXTRA $CPPFLAGS $LIBDIRS $SRC $EXTRA_CPP $EXTRA_SRC_OBJ $OUTPUT $LIBS"
LIBDIRS="-L$O3/clilib/dist-static-$PLATFORM/lib"
LIBS="-lovpncli -framework Foundation $LIBS"
SRC="$1.m $O3/cliobjc/ovpncli.mm "
CMD="$GPP_CMD $FLAGS $GCC_EXTRA $CPPFLAGS $LIBDIRS $SRC $EXTRA_SRC_OBJ $OUTPUT $LIBS"
else else
CMD="$GPP_CMD $FLAGS $GCC_EXTRA $CPPFLAGS $LIBDIRS $1.cpp $EXTRA_CPP $EXTRA_SRC_OBJ $OUTPUT $LIBS" CMD="$GPP_CMD $FLAGS $GCC_EXTRA $CPPFLAGS $LIBDIRS $1.cpp $EXTRA_CPP $EXTRA_SRC_OBJ $OUTPUT $LIBS"
fi fi
@@ -85,6 +85,15 @@ namespace {
class Client : public ClientAPI::OpenVPNClient class Client : public ClientAPI::OpenVPNClient
{ {
public: public:
enum ClockTickAction {
CT_UNDEF,
CT_STOP,
CT_RECONNECT,
CT_PAUSE,
CT_RESUME,
CT_STATS,
};
bool is_dynamic_challenge() const bool is_dynamic_challenge() const
{ {
return !dc_cookie.empty(); return !dc_cookie.empty();
@@ -101,14 +110,33 @@ public:
MbedTLSPKI::PKContext epki_ctx; // external PKI context MbedTLSPKI::PKContext epki_ctx; // external PKI context
#endif #endif
void set_clock_tick_action(const ClockTickAction action)
{
clock_tick_action = action;
}
void print_stats()
{
const int n = stats_n();
std::vector<long long> stats = stats_bundle();
std::cout << "STATS:" << std::endl;
for (int i = 0; i < n; ++i)
{
const long long value = stats[i];
if (value)
std::cout << " " << stats_name(i) << " : " << value << std::endl;
}
}
private: private:
virtual bool socket_protect(int socket) virtual bool socket_protect(int socket) override
{ {
std::cout << "*** socket_protect " << socket << std::endl; std::cout << "*** socket_protect " << socket << std::endl;
return true; return true;
} }
virtual void event(const ClientAPI::Event& ev) virtual void event(const ClientAPI::Event& ev) override
{ {
std::cout << date_time() << " EVENT: " << ev.name; std::cout << date_time() << " EVENT: " << ev.name;
if (!ev.info.empty()) if (!ev.info.empty())
@@ -155,13 +183,45 @@ private:
} }
} }
virtual void log(const ClientAPI::LogInfo& log) virtual void log(const ClientAPI::LogInfo& log) override
{ {
std::lock_guard<std::mutex> lock(log_mutex); std::lock_guard<std::mutex> lock(log_mutex);
std::cout << date_time() << ' ' << log.text << std::flush; std::cout << date_time() << ' ' << log.text << std::flush;
} }
virtual void external_pki_cert_request(ClientAPI::ExternalPKICertRequest& certreq) virtual void clock_tick() override
{
const ClockTickAction action = clock_tick_action;
clock_tick_action = CT_UNDEF;
switch (action)
{
case CT_STOP:
std::cout << "signal: CT_STOP" << std::endl;
stop();
break;
case CT_RECONNECT:
std::cout << "signal: CT_RECONNECT" << std::endl;
reconnect(0);
break;
case CT_PAUSE:
std::cout << "signal: CT_PAUSE" << std::endl;
pause("clock-tick pause");
break;
case CT_RESUME:
std::cout << "signal: CT_RESUME" << std::endl;
resume();
break;
case CT_STATS:
std::cout << "signal: CT_STATS" << std::endl;
print_stats();
break;
default:
break;
}
}
virtual void external_pki_cert_request(ClientAPI::ExternalPKICertRequest& certreq) override
{ {
if (!epki_cert.empty()) if (!epki_cert.empty())
{ {
@@ -175,7 +235,7 @@ private:
} }
} }
virtual void external_pki_sign_request(ClientAPI::ExternalPKISignRequest& signreq) virtual void external_pki_sign_request(ClientAPI::ExternalPKISignRequest& signreq) override
{ {
#if defined(USE_MBEDTLS) #if defined(USE_MBEDTLS)
if (epki_ctx.defined()) if (epki_ctx.defined())
@@ -239,7 +299,7 @@ private:
return self->rng->rand_bytes_noexcept(data, len) ? 0 : -1; // using -1 as a general-purpose mbed TLS error code return self->rng->rand_bytes_noexcept(data, len) ? 0 : -1; // using -1 as a general-purpose mbed TLS error code
} }
virtual bool pause_on_connection_timeout() virtual bool pause_on_connection_timeout() override
{ {
return false; return false;
} }
@@ -247,13 +307,16 @@ private:
std::mutex log_mutex; std::mutex log_mutex;
std::string dc_cookie; std::string dc_cookie;
RandomAPI::Ptr rng; // random data source for epki RandomAPI::Ptr rng; // random data source for epki
volatile ClockTickAction clock_tick_action = CT_UNDEF;
}; };
static Client *the_client = nullptr; // GLOBAL static Client *the_client = nullptr; // GLOBAL
static void worker_thread() static void worker_thread()
{ {
#if !defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
openvpn_io::detail::signal_blocker signal_blocker; // signals should be handled by parent thread openvpn_io::detail::signal_blocker signal_blocker; // signals should be handled by parent thread
#endif
try { try {
std::cout << "Thread starting..." << std::endl; std::cout << "Thread starting..." << std::endl;
ClientAPI::Status connect_status = the_client->connect(); ClientAPI::Status connect_status = the_client->connect();
@@ -272,21 +335,117 @@ static void worker_thread()
std::cout << "Thread finished" << std::endl; std::cout << "Thread finished" << std::endl;
} }
static void print_stats(const Client& client) static std::string read_profile(const char *fn, const std::string* profile_content)
{ {
const int n = client.stats_n(); if (!string::strcasecmp(fn, "http") && profile_content && !profile_content->empty())
std::vector<long long> stats = client.stats_bundle(); return *profile_content;
else
std::cout << "STATS:" << std::endl;
for (int i = 0; i < n; ++i)
{ {
const long long value = stats[i]; ProfileMerge pm(fn, "ovpn", "", ProfileMerge::FOLLOW_FULL,
if (value) ProfileParseLimits::MAX_LINE_SIZE, ProfileParseLimits::MAX_PROFILE_SIZE);
std::cout << " " << client.stats_name(i) << " : " << value << std::endl; if (pm.status() != ProfileMerge::MERGE_SUCCESS)
OPENVPN_THROW_EXCEPTION("merge config error: " << pm.status_string() << " : " << pm.error());
return pm.profile_content();
} }
} }
#if !defined(OPENVPN_PLATFORM_WIN) #if defined(OPENVPN_PLATFORM_WIN)
static void start_thread(Client& client)
{
// Set Windows title bar
const std::string title_text = "F2:Stats F3:Reconnect F4:Stop F5:Pause";
Win::Console::Title title(ClientAPI::OpenVPNClient::platform() + " " + title_text);
Win::Console::Input console;
// start connect thread
std::unique_ptr<std::thread> thread;
volatile bool thread_exit = false;
the_client = &client;
thread.reset(new std::thread([&thread_exit]() {
worker_thread();
thread_exit = true;
}));
// wait for connect thread to exit, also check for keypresses
while (!thread_exit)
{
while (true)
{
const unsigned int c = console.get();
if (!c)
break;
else if (c == 0x3C) // F2
the_client->print_stats();
else if (c == 0x3D) // F3
the_client->reconnect(0);
else if (c == 0x3E) // F4
the_client->stop();
else if (c == 0x3F) // F5
the_client->pause("user-pause");
}
Sleep(1000);
}
// wait for connect thread to exit
thread->join();
the_client = nullptr;
}
#elif defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
static void handler(int signum)
{
switch (signum)
{
case SIGTERM:
case SIGINT:
if (the_client)
the_client->set_clock_tick_action(Client::CT_STOP);
break;
case SIGHUP:
if (the_client)
the_client->set_clock_tick_action(Client::CT_RECONNECT);
break;
case SIGUSR1:
if (the_client)
the_client->set_clock_tick_action(Client::CT_STATS);
break;
case SIGUSR2:
{
// toggle pause/resume
static bool hup = false;
if (the_client)
{
if (hup)
the_client->set_clock_tick_action(Client::CT_RESUME);
else
the_client->set_clock_tick_action(Client::CT_PAUSE);
hup = !hup;
}
}
break;
default:
break;
}
}
static void start_thread(Client& client)
{
the_client = &client;
// capture signals that might occur while we're in worker_thread
Signal signal(handler, Signal::F_SIGINT|Signal::F_SIGTERM|Signal::F_SIGHUP|Signal::F_SIGUSR1|Signal::F_SIGUSR2);
// run the client
worker_thread();
the_client = nullptr;
}
#else
static void handler(int signum) static void handler(int signum)
{ {
switch (signum) switch (signum)
@@ -304,7 +463,7 @@ static void handler(int signum)
break; break;
case SIGUSR1: case SIGUSR1:
if (the_client) if (the_client)
print_stats(*the_client); the_client->print_stats();
break; break;
case SIGUSR2: case SIGUSR2:
{ {
@@ -326,22 +485,29 @@ static void handler(int signum)
break; break;
} }
} }
#endif
static std::string read_profile(const char *fn, const std::string* profile_content) static void start_thread(Client& client)
{ {
if (!string::strcasecmp(fn, "http") && profile_content && !profile_content->empty()) std::unique_ptr<std::thread> thread;
return *profile_content;
else // start connect thread
{ the_client = &client;
ProfileMerge pm(fn, "ovpn", "", ProfileMerge::FOLLOW_FULL, thread.reset(new std::thread([]() {
ProfileParseLimits::MAX_LINE_SIZE, ProfileParseLimits::MAX_PROFILE_SIZE); worker_thread();
if (pm.status() != ProfileMerge::MERGE_SUCCESS) }));
OPENVPN_THROW_EXCEPTION("merge config error: " << pm.status_string() << " : " << pm.error());
return pm.profile_content(); {
} // catch signals that might occur while we're in join()
Signal signal(handler, Signal::F_SIGINT|Signal::F_SIGTERM|Signal::F_SIGHUP|Signal::F_SIGUSR1|Signal::F_SIGUSR2);
// wait for connect thread to exit
thread->join();
}
the_client = nullptr;
} }
#endif
int openvpn_client(int argc, char *argv[], const std::string* profile_content) int openvpn_client(int argc, char *argv[], const std::string* profile_content)
{ {
static const struct option longopts[] = { static const struct option longopts[] = {
@@ -388,7 +554,6 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
auto cleanup = Cleanup([]() { auto cleanup = Cleanup([]() {
the_client = nullptr; the_client = nullptr;
}); });
std::unique_ptr<std::thread> thread;
try { try {
if (argc >= 2) if (argc >= 2)
@@ -618,6 +783,9 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
config.tunPersist = tunPersist; config.tunPersist = tunPersist;
config.gremlinConfig = gremlin; config.gremlinConfig = gremlin;
config.info = true; config.info = true;
#if defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
config.clockTickMS = 250;
#endif
if (!epki_cert_fn.empty()) if (!epki_cert_fn.empty())
config.externalPkiAlias = "epki"; // dummy string config.externalPkiAlias = "epki"; // dummy string
@@ -694,60 +862,8 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
std::cout << "CONNECTING..." << std::endl; std::cout << "CONNECTING..." << std::endl;
#if !defined(OPENVPN_PLATFORM_WIN) // start the client thread
// start connect thread start_thread(client);
the_client = &client;
thread.reset(new std::thread([]() {
worker_thread();
}));
{
// catch signals that might occur while we're in join()
Signal signal(handler, Signal::F_SIGINT|Signal::F_SIGTERM|Signal::F_SIGHUP|Signal::F_SIGUSR1|Signal::F_SIGUSR2);
// wait for connect thread to exit
thread->join();
}
the_client = nullptr;
#else
// Set Windows title bar
const std::string title_text = "F2:Stats F3:Reconnect F4:Stop F5:Pause";
Win::Console::Title title(ClientAPI::OpenVPNClient::platform() + " " + title_text);
Win::Console::Input console;
// start connect thread
volatile bool thread_exit = false;
the_client = &client;
thread.reset(new std::thread([&thread_exit]() {
worker_thread();
thread_exit = true;
}));
// wait for connect thread to exit, also check for keypresses
while (!thread_exit)
{
while (true)
{
const unsigned int c = console.get();
if (!c)
break;
else if (c == 0x3C) // F2
print_stats(*the_client);
else if (c == 0x3D) // F3
the_client->reconnect(0);
else if (c == 0x3E) // F4
the_client->stop();
else if (c == 0x3F) // F5
the_client->pause("user-pause");
}
Sleep(1000);
}
// wait for connect thread to exit
thread->join();
the_client = nullptr;
#endif
// Get dynamic challenge response // Get dynamic challenge response
if (client.is_dynamic_challenge()) if (client.is_dynamic_challenge())
@@ -763,7 +879,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
else else
{ {
// print closing stats // print closing stats
print_stats(client); client.print_stats();
} }
} }
} while (retry); } while (retry);