mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
// 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 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/>.
|
||||
|
||||
// The file should include any platform-specific files necessary
|
||||
// to declare the std::abort() function.
|
||||
|
||||
#ifndef OPENVPN_COMMON_ABORT_H
|
||||
#define OPENVPN_COMMON_ABORT_H
|
||||
|
||||
#include <cstdlib> // defines std::abort()
|
||||
|
||||
#endif // OPENVPN_COMMON_ABORT_H
|
||||
@@ -0,0 +1,212 @@
|
||||
// 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 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_COMMON_ACTION_H
|
||||
#define OPENVPN_COMMON_ACTION_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/destruct.hpp>
|
||||
#include <openvpn/common/jsonlib.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct Action : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Action> Ptr;
|
||||
|
||||
virtual void execute(std::ostream& os) = 0;
|
||||
virtual std::string to_string() const = 0;
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() const
|
||||
{
|
||||
throw Exception("Action::to_json() virtual method not implemented");
|
||||
}
|
||||
#endif
|
||||
virtual ~Action() {}
|
||||
};
|
||||
|
||||
class ActionList : public std::vector<Action::Ptr>, public DestructorBase
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ActionList> Ptr;
|
||||
|
||||
ActionList()
|
||||
{
|
||||
reserve(16);
|
||||
}
|
||||
|
||||
void add(Action* action)
|
||||
{
|
||||
if (action)
|
||||
emplace_back(action);
|
||||
}
|
||||
|
||||
void add(const Action::Ptr& action)
|
||||
{
|
||||
if (action)
|
||||
push_back(action);
|
||||
}
|
||||
|
||||
void add(const ActionList& other)
|
||||
{
|
||||
insert(end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
bool exists(const Action::Ptr& action) const
|
||||
{
|
||||
if (action)
|
||||
{
|
||||
const std::string cmp = action->to_string();
|
||||
for (auto &a : *this)
|
||||
{
|
||||
if (a->to_string() == cmp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os)
|
||||
{
|
||||
Iter i(size(), reverse_);
|
||||
while (i())
|
||||
{
|
||||
if (is_halt())
|
||||
return;
|
||||
try {
|
||||
(*this)[i.index()]->execute(os);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
os << "action exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void execute_log()
|
||||
{
|
||||
std::ostringstream os;
|
||||
execute(os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
Iter i(size(), reverse_);
|
||||
while (i())
|
||||
{
|
||||
ret += (*this)[i.index()]->to_string();
|
||||
ret += '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void enable_destroy(const bool state)
|
||||
{
|
||||
enable_destroy_ = state;
|
||||
}
|
||||
|
||||
void halt()
|
||||
{
|
||||
halt_ = true;
|
||||
}
|
||||
|
||||
virtual void destroy(std::ostream& os) override // defined by DestructorBase
|
||||
{
|
||||
if (enable_destroy_)
|
||||
{
|
||||
execute(os);
|
||||
enable_destroy_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_halt() const
|
||||
{
|
||||
return halt_;
|
||||
}
|
||||
|
||||
protected:
|
||||
class Iter
|
||||
{
|
||||
public:
|
||||
Iter(size_t size, const bool reverse)
|
||||
{
|
||||
if (reverse)
|
||||
{
|
||||
idx = size;
|
||||
end = -1;
|
||||
delta = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = -1;
|
||||
end = size;
|
||||
delta = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator()()
|
||||
{
|
||||
return (idx += delta) != end;
|
||||
}
|
||||
|
||||
size_t index() const
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t idx;
|
||||
size_t end;
|
||||
size_t delta;
|
||||
};
|
||||
|
||||
bool reverse_ = false;
|
||||
bool enable_destroy_ = false;
|
||||
volatile bool halt_ = false;
|
||||
};
|
||||
|
||||
struct ActionListReversed : public ActionList
|
||||
{
|
||||
ActionListReversed()
|
||||
{
|
||||
reverse_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
struct ActionListFactory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<ActionListFactory> Ptr;
|
||||
|
||||
virtual ActionList::Ptr new_action_list() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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 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_COMMON_ACTIONTHREAD_H
|
||||
#define OPENVPN_COMMON_ACTIONTHREAD_H
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/log/logthread.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ActionThread : public RC<thread_safe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ActionThread> Ptr;
|
||||
|
||||
struct Notify
|
||||
{
|
||||
virtual void action_thread_finished(const ActionThread* self, bool status) = 0;
|
||||
};
|
||||
|
||||
ActionThread(openvpn_io::io_context& io_context_arg,
|
||||
const ActionList::Ptr& action_list,
|
||||
Notify* completion_handler_arg)
|
||||
: io_context(io_context_arg),
|
||||
thread(nullptr),
|
||||
actions(action_list),
|
||||
completion_handler(completion_handler_arg)
|
||||
{
|
||||
if (actions)
|
||||
thread = new std::thread(&ActionThread::thread_func, this);
|
||||
}
|
||||
|
||||
void stop(const bool halt)
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
if (halt)
|
||||
actions->halt();
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = nullptr;
|
||||
// Necessary because no guarantee that completion_handler
|
||||
// obj will remain in scope during io_context.post delay.
|
||||
completion_handler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~ActionThread()
|
||||
{
|
||||
stop(true);
|
||||
}
|
||||
|
||||
private:
|
||||
void completion_post(bool status)
|
||||
{
|
||||
Notify* n = completion_handler;
|
||||
completion_handler = nullptr;
|
||||
if (n)
|
||||
n->action_thread_finished(this, status);
|
||||
}
|
||||
|
||||
void thread_func()
|
||||
{
|
||||
Log::Context logctx(logwrap);
|
||||
bool status = false;
|
||||
try {
|
||||
OPENVPN_LOG("START THREAD...");
|
||||
status = actions->execute();
|
||||
OPENVPN_LOG("END THREAD");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("ActionThread exception: " << e.what());
|
||||
}
|
||||
openvpn_io::post(io_context, [self=Ptr(this), status]()
|
||||
{
|
||||
self->completion_post(status);
|
||||
});
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
std::thread* thread;
|
||||
ActionList::Ptr actions; // actions to execute in child thread
|
||||
Notify* completion_handler; // completion handler
|
||||
Log::Context::Wrapper logwrap; // used to carry forward the log context from parent thread
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 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_COMMON_APPVERSION_H
|
||||
#define OPENVPN_COMMON_APPVERSION_H
|
||||
|
||||
// BUILD_VERSION version can be passed on build command line
|
||||
|
||||
#include <openvpn/common/stringize.hpp>
|
||||
|
||||
#ifdef BUILD_VERSION
|
||||
#define MY_VERSION OPENVPN_STRINGIZE(BUILD_VERSION)
|
||||
#else
|
||||
#define MY_VERSION "0.1.0"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 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/>.
|
||||
|
||||
// define an ARCH_x macro that describes our target architecture
|
||||
|
||||
#ifndef OPENVPN_COMMON_ARCH_H
|
||||
#define OPENVPN_COMMON_ARCH_H
|
||||
|
||||
#if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
|
||||
# define OPENVPN_ARCH_x86_64
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
# define OPENVPN_ARCH_i386
|
||||
#elif defined(__aarch64__) || defined(__arm64__)
|
||||
# define OPENVPN_ARCH_ARM64
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
# define OPENVPN_ARCH_ARM
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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 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_COMMON_ARGV_H
|
||||
#define OPENVPN_COMMON_ARGV_H
|
||||
|
||||
#include <cstring> // memcpy
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class Argv : public std::vector<std::string>
|
||||
{
|
||||
public:
|
||||
Argv(const size_t capacity=16)
|
||||
{
|
||||
reserve(capacity);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
bool first = true;
|
||||
for (const auto &s : *this)
|
||||
{
|
||||
if (!first)
|
||||
ret += ' ';
|
||||
ret += s;
|
||||
first = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class ArgvWrapper
|
||||
{
|
||||
public:
|
||||
explicit ArgvWrapper(const std::vector<std::string>& argv)
|
||||
{
|
||||
size_t i;
|
||||
argc = argv.size();
|
||||
cargv = new char *[argc+1];
|
||||
for (i = 0; i < argc; ++i)
|
||||
cargv[i] = string_alloc(argv[i]);
|
||||
cargv[i] = nullptr;
|
||||
}
|
||||
|
||||
ArgvWrapper(ArgvWrapper&& rhs) noexcept
|
||||
{
|
||||
argc = rhs.argc;
|
||||
cargv = rhs.cargv;
|
||||
rhs.argc = 0;
|
||||
rhs.cargv = nullptr;
|
||||
}
|
||||
|
||||
ArgvWrapper& operator=(ArgvWrapper&& rhs) noexcept
|
||||
{
|
||||
del();
|
||||
argc = rhs.argc;
|
||||
cargv = rhs.cargv;
|
||||
rhs.argc = 0;
|
||||
rhs.cargv = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ArgvWrapper()
|
||||
{
|
||||
del();
|
||||
}
|
||||
|
||||
char *const *c_argv() const noexcept
|
||||
{
|
||||
return cargv;
|
||||
}
|
||||
|
||||
char **c_argv() noexcept
|
||||
{
|
||||
return cargv;
|
||||
}
|
||||
|
||||
size_t c_argc() const noexcept
|
||||
{
|
||||
return argc;
|
||||
}
|
||||
|
||||
private:
|
||||
ArgvWrapper(const ArgvWrapper&) = delete;
|
||||
ArgvWrapper& operator=(const ArgvWrapper&) = delete;
|
||||
|
||||
static char *string_alloc(const std::string& s)
|
||||
{
|
||||
const char *sdata = s.c_str();
|
||||
const size_t slen = s.length();
|
||||
char *ret = new char[slen+1];
|
||||
std::memcpy(ret, sdata, slen);
|
||||
ret[slen] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
void del()
|
||||
{
|
||||
for (size_t i = 0; i < argc; ++i)
|
||||
delete [] cargv[i];
|
||||
delete [] cargv;
|
||||
}
|
||||
|
||||
size_t argc;
|
||||
char **cargv;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#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 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_COMMON_ARRAYSIZE_H
|
||||
#define OPENVPN_COMMON_ARRAYSIZE_H
|
||||
|
||||
#include <cstddef> // defines size_t
|
||||
|
||||
namespace openvpn {
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::size_t array_size( T (&)[N] ) {
|
||||
return N;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Interruptible sleep
|
||||
|
||||
#ifndef OPENVPN_COMMON_ASYNCSLEEP_H
|
||||
#define OPENVPN_COMMON_ASYNCSLEEP_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/common/sleep.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// returns false if Stop signal prevented full wait
|
||||
inline bool async_sleep_milliseconds(int milliseconds, Stop* async_stop)
|
||||
{
|
||||
const int milliseconds_per_retry = 250;
|
||||
volatile bool stop = false;
|
||||
|
||||
// allow asynchronous stop
|
||||
Stop::Scope stop_scope(async_stop, [&stop]() {
|
||||
stop = true;
|
||||
});
|
||||
|
||||
while (milliseconds > 0 && !stop)
|
||||
{
|
||||
const int ms = std::min(milliseconds, milliseconds_per_retry);
|
||||
sleep_milliseconds(ms);
|
||||
milliseconds -= ms;
|
||||
}
|
||||
|
||||
return !stop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Automatically reset a target object when
|
||||
// AutoReset goes out of scope.
|
||||
|
||||
#ifndef OPENVPN_COMMON_AUTORESET_H
|
||||
#define OPENVPN_COMMON_AUTORESET_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
class AutoReset
|
||||
{
|
||||
public:
|
||||
AutoReset(T& obj)
|
||||
: obj_(&obj)
|
||||
{}
|
||||
|
||||
~AutoReset()
|
||||
{
|
||||
if (obj_)
|
||||
obj_->reset();
|
||||
}
|
||||
|
||||
void disarm()
|
||||
{
|
||||
obj_ = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* obj_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,318 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General-purpose base64 encode and decode.
|
||||
|
||||
#ifndef OPENVPN_COMMON_BASE64_H
|
||||
#define OPENVPN_COMMON_BASE64_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring> // for std::memset, std::strlen
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/extern.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class Base64 {
|
||||
|
||||
class ConstUCharWrap
|
||||
{
|
||||
public:
|
||||
ConstUCharWrap(const unsigned char *data, size_t size)
|
||||
: data_(data),
|
||||
size_(size)
|
||||
{}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
unsigned char operator[](const size_t i) const { return data_[i]; }
|
||||
|
||||
|
||||
private:
|
||||
const unsigned char *data_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_out_of_bound_error);
|
||||
|
||||
private:
|
||||
// Minimal class to minic the container our decode method expects
|
||||
class UCharWrap
|
||||
{
|
||||
public:
|
||||
UCharWrap(unsigned char *data, size_t size):
|
||||
data(data), size(size), index(0)
|
||||
{
|
||||
}
|
||||
|
||||
void push_back(unsigned char c)
|
||||
{
|
||||
if (index >= size)
|
||||
throw base64_decode_out_of_bound_error();
|
||||
|
||||
data[index++]=c;
|
||||
}
|
||||
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_bad_map);
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_error);
|
||||
|
||||
// altmap is "+/=" by default
|
||||
// another possible encoding for URLs: "-_."
|
||||
Base64(const char *altmap = nullptr)
|
||||
{
|
||||
// build encoding map
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int j = 65;
|
||||
for (i = 0; i < 62; ++i)
|
||||
{
|
||||
enc[i] = j++;
|
||||
if (j == 91)
|
||||
j = 97;
|
||||
else if (j == 123)
|
||||
j = 48;
|
||||
}
|
||||
if (!altmap)
|
||||
altmap = "+/=";
|
||||
if (std::strlen(altmap) != 3)
|
||||
throw base64_bad_map();
|
||||
enc[62] = (unsigned char)altmap[0];
|
||||
enc[63] = (unsigned char)altmap[1];
|
||||
equal = (unsigned char)altmap[2];
|
||||
}
|
||||
|
||||
// build decoding map
|
||||
{
|
||||
std::memset(dec, 0xFF, 128);
|
||||
for (unsigned int i = 0; i < 64; ++i)
|
||||
{
|
||||
const unsigned char c = enc[i];
|
||||
if (c >= 128)
|
||||
throw base64_bad_map();
|
||||
dec[c] = (unsigned char)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t decode_size_max(const size_t encode_size)
|
||||
{
|
||||
return encode_size;
|
||||
}
|
||||
|
||||
static size_t encode_size_max(const size_t decode_size)
|
||||
{
|
||||
return decode_size * 4 / 3 + 4;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
std::string encode(const V& data) const
|
||||
{
|
||||
char *s, *p;
|
||||
size_t i;
|
||||
unsigned int c;
|
||||
const size_t size = data.size();
|
||||
|
||||
p = s = new char[encode_size_max(size)];
|
||||
for (i = 0; i < size; ) {
|
||||
c = static_cast<unsigned char>(data[i++]) << 8;
|
||||
if (i < size)
|
||||
c += static_cast<unsigned char>(data[i]);
|
||||
i++;
|
||||
c <<= 8;
|
||||
if (i < size)
|
||||
c += static_cast<unsigned char>(data[i]);
|
||||
i++;
|
||||
p[0] = enc[(c & 0x00fc0000) >> 18];
|
||||
p[1] = enc[(c & 0x0003f000) >> 12];
|
||||
p[2] = enc[(c & 0x00000fc0) >> 6];
|
||||
p[3] = enc[c & 0x0000003f];
|
||||
if (i > size)
|
||||
p[3] = equal;
|
||||
if (i > size + 1)
|
||||
p[2] = equal;
|
||||
p += 4;
|
||||
}
|
||||
*p = '\0';
|
||||
const std::string ret(s);
|
||||
delete [] s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string encode(const void *data, size_t size) const
|
||||
{
|
||||
return encode(ConstUCharWrap((const unsigned char *)data, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes data from the passed string and stores the
|
||||
* result in data
|
||||
* @param data Destination of the decoded data
|
||||
* @param len Length of the region in data
|
||||
* @param str Base64 string to decode
|
||||
* @return Number of bytes written to data
|
||||
*/
|
||||
size_t decode(void *data, size_t len, const std::string& str) const
|
||||
{
|
||||
UCharWrap ret((unsigned char*)data, len);
|
||||
decode(ret, str);
|
||||
return ret.index;
|
||||
}
|
||||
|
||||
std::string decode(const std::string& str) const
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length());
|
||||
decode(ret, str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void decode(V& dest, const std::string& str) const
|
||||
{
|
||||
for (const char *p = str.c_str(); *p != '\0' && (*p == equal || is_base64_char(*p)); p += 4)
|
||||
{
|
||||
unsigned int marker;
|
||||
const unsigned int val = token_decode(p, marker);
|
||||
dest.push_back((val >> 16) & 0xff);
|
||||
if (marker < 2)
|
||||
dest.push_back((val >> 8) & 0xff);
|
||||
if (marker < 1)
|
||||
dest.push_back(val & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
bool is_base64(const V& data, const size_t expected_decoded_length) const
|
||||
{
|
||||
const size_t size = data.size();
|
||||
if (size != encoded_len(expected_decoded_length))
|
||||
return false;
|
||||
const size_t eq_begin = size - num_eq(expected_decoded_length);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
const char c = data[i];
|
||||
if (i < eq_begin)
|
||||
{
|
||||
if (!is_base64_char(c))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c != equal)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_base64_char(const char c) const
|
||||
{
|
||||
const size_t idx = c;
|
||||
return idx < 128 && dec[idx] != 0xFF;
|
||||
}
|
||||
|
||||
unsigned int decode_base64_char(const char c) const
|
||||
{
|
||||
const size_t idx = c;
|
||||
if (idx >= 128)
|
||||
throw base64_decode_error();
|
||||
const unsigned int v = dec[idx];
|
||||
if (v == 0xFF)
|
||||
throw base64_decode_error();
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned int token_decode(const char *token, unsigned int& marker) const
|
||||
{
|
||||
size_t i;
|
||||
unsigned int val = 0;
|
||||
marker = 0; // number of equal chars seen
|
||||
if (std::strlen(token) < 4)
|
||||
throw base64_decode_error();
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
val <<= 6;
|
||||
if (token[i] == equal)
|
||||
marker++;
|
||||
else if (marker > 0)
|
||||
throw base64_decode_error();
|
||||
else
|
||||
val += decode_base64_char(token[i]);
|
||||
}
|
||||
if (marker > 2)
|
||||
throw base64_decode_error();
|
||||
return val;
|
||||
}
|
||||
|
||||
static size_t encoded_len(const size_t decoded_len)
|
||||
{
|
||||
return (decoded_len * 4 / 3 + 3) & ~3;
|
||||
}
|
||||
|
||||
static size_t num_eq(const size_t decoded_len)
|
||||
{
|
||||
return (-1 - decoded_len) % 3;
|
||||
}
|
||||
|
||||
unsigned char enc[64];
|
||||
unsigned char dec[128];
|
||||
unsigned char equal;
|
||||
};
|
||||
|
||||
// provide a static Base64 object
|
||||
|
||||
OPENVPN_EXTERN const Base64* base64; // GLOBAL
|
||||
OPENVPN_EXTERN const Base64* base64_urlsafe; // GLOBAL
|
||||
|
||||
inline void base64_init_static()
|
||||
{
|
||||
if (!base64)
|
||||
base64 = new Base64();
|
||||
if (!base64_urlsafe)
|
||||
base64_urlsafe = new Base64("-_.");
|
||||
}
|
||||
|
||||
inline void base64_uninit_static()
|
||||
{
|
||||
if (base64)
|
||||
{
|
||||
delete base64;
|
||||
base64 = nullptr;
|
||||
}
|
||||
if (base64_urlsafe)
|
||||
{
|
||||
delete base64_urlsafe;
|
||||
base64_urlsafe = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Macro to maintain thread-safety.
|
||||
|
||||
// Platforms like UWP and iOS may call core methods
|
||||
// from another threads. Since core is not thread-safe,
|
||||
// we provide OPENVPN_ASYNC_HANDLER macro which instantiates
|
||||
// lock guard. It follows RIAA principle and locks global
|
||||
// mutex in constructor and unlocks in destructor. This
|
||||
// guarantees that code in block protected with this macro
|
||||
// won't be called simultaneously from different threads.
|
||||
|
||||
#ifndef OPENVPN_COMMON_BIGMUTEX_H
|
||||
#define OPENVPN_COMMON_BIGMUTEX_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <openvpn/common/extern.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace bigmutex {
|
||||
OPENVPN_EXTERN std::recursive_mutex the_recursive_mutex;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_ENABLE_BIGMUTEX
|
||||
#define OPENVPN_ASYNC_HANDLER \
|
||||
std::lock_guard<std::recursive_mutex> lg(bigmutex::the_recursive_mutex);
|
||||
#else
|
||||
#define OPENVPN_ASYNC_HANDLER
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 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_COMMON_BINPREFIX_H
|
||||
#define OPENVPN_COMMON_BINPREFIX_H
|
||||
|
||||
#include <algorithm> // for std::min, std::max
|
||||
#include <cstring> // for std::memset, std::memcpy
|
||||
#include <cstdint> // for std::uint32_t, uint64_t
|
||||
|
||||
#include <openvpn/common/socktypes.hpp> // for ntohl
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Return the binary prefix of a big-endian data buffer
|
||||
// as a 32 or 64 bit type.
|
||||
|
||||
template <typename T>
|
||||
inline T bin_prefix(const unsigned char *data)
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8, "size inconsistency");
|
||||
if (sizeof(T) == 8)
|
||||
return (T(ntohl(*(uint32_t *)&data[0])) << 32) | T(ntohl(*(uint32_t *)&data[4]));
|
||||
else // sizeof(T) == 4
|
||||
return T(ntohl(*(uint32_t *)&data[0]));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T bin_prefix(const unsigned char *data, const size_t len)
|
||||
{
|
||||
unsigned char d[sizeof(T)]
|
||||
#ifndef _MSC_VER
|
||||
__attribute__((aligned(sizeof(T))))
|
||||
#endif
|
||||
;
|
||||
const size_t l = std::min(len, sizeof(d));
|
||||
std::memset(d, 0, sizeof(d));
|
||||
std::memcpy(d + sizeof(d) - l, data, l);
|
||||
return bin_prefix<T>(d);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T bin_prefix_floor(const unsigned char *data, const size_t len, const T floor)
|
||||
{
|
||||
return std::max(bin_prefix<T>(data, len), floor);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A general-purpose circular list collection class.
|
||||
// Used by the OpenVPN anti-replay logic.
|
||||
|
||||
#ifndef OPENVPN_COMMON_CIRC_LIST_H
|
||||
#define OPENVPN_COMMON_CIRC_LIST_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
class CircList
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_reset);
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_index);
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_const_index);
|
||||
OPENVPN_SIMPLE_EXCEPTION(circ_list_push);
|
||||
|
||||
CircList() { init(0); }
|
||||
|
||||
explicit CircList(const size_t capacity) { init(capacity); }
|
||||
|
||||
void init(const size_t capacity)
|
||||
{
|
||||
if (capacity)
|
||||
{
|
||||
data_.reserve(capacity);
|
||||
capacity_ = capacity;
|
||||
reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
head_ = capacity_ = 0;
|
||||
data_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (capacity_)
|
||||
{
|
||||
head_ = capacity_ - 1;
|
||||
data_.clear();
|
||||
}
|
||||
else
|
||||
throw circ_list_reset();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return capacity_ > 0;
|
||||
}
|
||||
|
||||
void push(const T& item)
|
||||
{
|
||||
if (++head_ >= capacity_)
|
||||
head_ = 0;
|
||||
if (head_ < data_.size())
|
||||
data_[head_] = item;
|
||||
else if (head_ == data_.size() && data_.size() < capacity_)
|
||||
data_.push_back(item);
|
||||
else
|
||||
throw circ_list_push(); // could occur if object isn't properly initialized
|
||||
}
|
||||
|
||||
T& operator[](const size_t index)
|
||||
{
|
||||
if (index >= data_.size())
|
||||
throw circ_list_index();
|
||||
else if (index <= head_)
|
||||
return data_[head_-index];
|
||||
else
|
||||
return data_[head_+capacity_-index];
|
||||
}
|
||||
|
||||
const T& operator[](const size_t index) const
|
||||
{
|
||||
if (index >= data_.size())
|
||||
throw circ_list_const_index();
|
||||
else if (index <= head_)
|
||||
return data_[head_-index];
|
||||
else
|
||||
return data_[head_+capacity_-index];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t capacity_;
|
||||
size_t head_;
|
||||
std::vector<T> data_;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_CIRC_LIST_H
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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-2019 OpenVPN 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
// loose emulation of std::clamp for pre-C++17
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
T clamp(T value, T low, T high)
|
||||
{
|
||||
if (value < low)
|
||||
return low;
|
||||
else if (value > high)
|
||||
return high;
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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 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_COMMON_CLEANUP_H
|
||||
#define OPENVPN_COMMON_CLEANUP_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename F>
|
||||
class CleanupType
|
||||
{
|
||||
public:
|
||||
CleanupType(F method) noexcept
|
||||
: clean(std::move(method))
|
||||
{
|
||||
}
|
||||
|
||||
CleanupType(CleanupType&&) = default;
|
||||
|
||||
~CleanupType()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
private:
|
||||
CleanupType(const CleanupType&) = delete;
|
||||
CleanupType& operator=(const CleanupType&) = delete;
|
||||
|
||||
F clean;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
inline CleanupType<F> Cleanup(F method) noexcept
|
||||
{
|
||||
return CleanupType<F>(std::move(method));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Linux methods for enumerating the number of cores on machine,
|
||||
// and binding a thread to a particular core.
|
||||
|
||||
#ifndef OPENVPN_COMMON_CORE_H
|
||||
#define OPENVPN_COMMON_CORE_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_APPLE)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX)
|
||||
#include <unistd.h>
|
||||
#elif defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
inline int n_cores()
|
||||
{
|
||||
int count = std::thread::hardware_concurrency();
|
||||
// C++11 allows thread::hardware_concurrency() to return 0, fall back
|
||||
// to specific solution if we detect this
|
||||
if (count > 0)
|
||||
return count;
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_APPLE)
|
||||
size_t count_len = sizeof(count);
|
||||
if (::sysctlbyname("hw.logicalcpu", &count, &count_len, NULL, 0) != 0)
|
||||
count = 1;
|
||||
return count;
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX)
|
||||
long ret = ::sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (ret <= 0)
|
||||
ret = 1;
|
||||
return ret;
|
||||
#elif defined(OPENVPN_PLATFORM_WIN)
|
||||
SYSTEM_INFO si;
|
||||
::GetSystemInfo(&si);
|
||||
return si.dwNumberOfProcessors;
|
||||
#else
|
||||
#error no implementation for n_cores()
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 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_COMMON_COUNT_H
|
||||
#define OPENVPN_COMMON_COUNT_H
|
||||
|
||||
namespace openvpn {
|
||||
typedef long long count_t;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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 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_COMMON_DAEMON_H
|
||||
#define OPENVPN_COMMON_DAEMON_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/logrotate.hpp>
|
||||
#include <openvpn/common/redir.hpp>
|
||||
#include <openvpn/common/usergroup.hpp>
|
||||
#include <openvpn/common/logsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_EXCEPTION(daemon_err);
|
||||
|
||||
class LogReopen : public LogSetup
|
||||
{
|
||||
public:
|
||||
LogReopen(const std::string& log_fn,
|
||||
const bool combine_out_err)
|
||||
: log_fn_(log_fn),
|
||||
combine_out_err_(combine_out_err)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void reopen() const override
|
||||
{
|
||||
try {
|
||||
// open redirection log file, but don't redirect yet
|
||||
RedirectStd redir(std::string(),
|
||||
log_fn_,
|
||||
RedirectStd::FLAGS_APPEND,
|
||||
RedirectStd::MODE_USER_GROUP,
|
||||
combine_out_err_);
|
||||
|
||||
// now do the redirect
|
||||
redir.redirect();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "LogReopen: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string log_fn_;
|
||||
const bool combine_out_err_;
|
||||
};
|
||||
|
||||
inline LogSetup::Ptr log_setup(const std::string& log_fn,
|
||||
const SetUserGroup* user_group,
|
||||
const bool log_append,
|
||||
const int log_versions,
|
||||
const bool stdin_to_dev_null,
|
||||
const bool combine_out_err)
|
||||
{
|
||||
if (!log_append && log_versions >= 1)
|
||||
log_rotate(log_fn, log_versions);
|
||||
RedirectStd redir(stdin_to_dev_null ? "/dev/null" : "",
|
||||
log_fn,
|
||||
log_append ? RedirectStd::FLAGS_APPEND : RedirectStd::FLAGS_OVERWRITE,
|
||||
RedirectStd::MODE_USER_GROUP,
|
||||
combine_out_err);
|
||||
// if user_group specified, do chown on log file
|
||||
try {
|
||||
if (user_group && redir.out.defined())
|
||||
user_group->chown(redir.out(), log_fn);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
}
|
||||
redir.redirect();
|
||||
|
||||
// possibly return a LogReopen object
|
||||
if (!log_versions)
|
||||
return LogSetup::Ptr(new LogReopen(log_fn, combine_out_err));
|
||||
else
|
||||
return LogSetup::Ptr();
|
||||
}
|
||||
|
||||
inline void daemonize()
|
||||
{
|
||||
if (daemon(1, 1) < 0)
|
||||
throw daemon_err("daemon() failed");
|
||||
}
|
||||
|
||||
inline LogSetup::Ptr daemonize(const std::string& log_fn,
|
||||
const SetUserGroup* user_group,
|
||||
const bool log_append,
|
||||
const int log_versions)
|
||||
{
|
||||
LogSetup::Ptr ret = log_setup(log_fn, user_group, log_append, log_versions, true, true);
|
||||
daemonize();
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void write_pid(const std::string& fn)
|
||||
{
|
||||
write_string(fn, to_string(::getpid()) + '\n');
|
||||
}
|
||||
|
||||
class WritePid
|
||||
{
|
||||
public:
|
||||
WritePid(const char *pid_fn_arg) // must remain in scope for lifetime of object
|
||||
: pid_fn(pid_fn_arg)
|
||||
{
|
||||
if (pid_fn)
|
||||
write_pid(pid_fn);
|
||||
}
|
||||
|
||||
~WritePid()
|
||||
{
|
||||
if (pid_fn)
|
||||
::unlink(pid_fn);
|
||||
}
|
||||
|
||||
private:
|
||||
WritePid(const WritePid&) = delete;
|
||||
WritePid& operator=(const WritePid&) = delete;
|
||||
|
||||
const char *const pid_fn;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Demangle a C++ name (GCC only)
|
||||
|
||||
#ifndef OPENVPN_COMMON_DEMANGLE_H
|
||||
#define OPENVPN_COMMON_DEMANGLE_H
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
inline std::string cxx_demangle(const char *mangled_name)
|
||||
{
|
||||
int status;
|
||||
std::unique_ptr<char[]> realname;
|
||||
|
||||
realname.reset(abi::__cxa_demangle(mangled_name, 0, 0, &status));
|
||||
if (!status)
|
||||
return std::string(realname.get());
|
||||
else
|
||||
return "DEMANGLE_ERROR";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 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_COMMON_DESTRUCT_H
|
||||
#define OPENVPN_COMMON_DESTRUCT_H
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
// used for general-purpose cleanup
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct DestructorBase : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<DestructorBase> Ptr;
|
||||
virtual void destroy(std::ostream& os) = 0;
|
||||
virtual ~DestructorBase() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/endian_platform.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Endian {
|
||||
# if defined(OPENVPN_LITTLE_ENDIAN)
|
||||
inline size_t e16(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e16rev(const size_t v)
|
||||
{
|
||||
return 15-v;
|
||||
}
|
||||
inline size_t e4(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e4rev(const size_t v)
|
||||
{
|
||||
return 3-v;
|
||||
}
|
||||
inline size_t e2(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e2rev(const size_t v)
|
||||
{
|
||||
return 1-v;
|
||||
}
|
||||
# elif defined(OPENVPN_BIG_ENDIAN)
|
||||
inline size_t e16rev(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e16(const size_t v)
|
||||
{
|
||||
return 15-v;
|
||||
}
|
||||
inline size_t e4rev(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e4(const size_t v)
|
||||
{
|
||||
return 3-v;
|
||||
}
|
||||
inline size_t e2rev(const size_t v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
inline size_t e2(const size_t v)
|
||||
{
|
||||
return 1-v;
|
||||
}
|
||||
# else
|
||||
# error One of OPENVPN_LITTLE_ENDIAN or OPENVPN_BIG_ENDIAN must be defined
|
||||
# endif
|
||||
}
|
||||
} // namespace openvpn
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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-2018 OpenVPN 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
// test for machine endiannes
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__)
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define OPENVPN_BIG_ENDIAN
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define OPENVPN_LITTLE_ENDIAN
|
||||
#endif
|
||||
#elif defined(_WIN32)
|
||||
#define OPENVPN_LITTLE_ENDIAN // assume that Windows is always little-endian
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 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_COMMON_ENUMDIR_H
|
||||
#define OPENVPN_COMMON_ENUMDIR_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/uniqueptr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
OPENVPN_EXCEPTION(enum_dir_error);
|
||||
|
||||
template <typename F>
|
||||
inline bool enum_dir(const std::string& dirname,
|
||||
F func)
|
||||
{
|
||||
unique_ptr_del<DIR> dir(::opendir(dirname.c_str()), [](DIR* d) { ::closedir(d); });
|
||||
if (!dir)
|
||||
return false;
|
||||
|
||||
struct dirent *e;
|
||||
while ((e = ::readdir(dir.get())) != nullptr)
|
||||
{
|
||||
std::string fn(e->d_name);
|
||||
if (fn != "." && fn != "..")
|
||||
func(std::move(fn));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::vector<std::string> enum_dir(const std::string& dirname,
|
||||
const size_t size_hint=0,
|
||||
const bool sort=false)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
if (size_hint)
|
||||
ret.reserve(size_hint);
|
||||
|
||||
if (!enum_dir(dirname, [&ret](std::string fn) {
|
||||
ret.push_back(std::move(fn));
|
||||
}))
|
||||
throw enum_dir_error(dirname + ": cannot open directory");
|
||||
|
||||
if (sort)
|
||||
std::sort(ret.begin(), ret.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Environmental variables
|
||||
|
||||
#ifndef OPENVPN_COMMON_ENVIRON_H
|
||||
#define OPENVPN_COMMON_ENVIRON_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class Environ : public std::vector<std::string>
|
||||
{
|
||||
public:
|
||||
static std::string find_static(const std::string& name)
|
||||
{
|
||||
for (char **e = ::environ; *e != NULL; ++e)
|
||||
{
|
||||
const char *eq = ::strchr(*e, '=');
|
||||
if (eq && eq > *e)
|
||||
{
|
||||
const size_t namelen = eq - *e;
|
||||
if (name.length() == namelen && ::strncmp(name.c_str(), *e, namelen) == 0)
|
||||
return std::string(eq+1);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void load_from_environ()
|
||||
{
|
||||
reserve(64);
|
||||
for (char **e = ::environ; *e != NULL; ++e)
|
||||
emplace_back(*e);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(512);
|
||||
for (const auto &s : *this)
|
||||
{
|
||||
ret += s;
|
||||
ret += '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int find_index(const std::string& name) const
|
||||
{
|
||||
for (int i = 0; i < size(); ++i)
|
||||
{
|
||||
const std::string& s = (*this)[i];
|
||||
const size_t pos = s.find_first_of('=');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (name == s.substr(0, pos))
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name == s)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string find(const std::string& name) const
|
||||
{
|
||||
const int i = find_index(name);
|
||||
if (i >= 0)
|
||||
return value(i);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string value(const size_t idx) const
|
||||
{
|
||||
const std::string& s = (*this)[idx];
|
||||
const size_t pos = s.find_first_of('=');
|
||||
if (pos != std::string::npos)
|
||||
return s.substr(pos+1);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void assign(const std::string& name, const std::string& value)
|
||||
{
|
||||
std::string nv = name + '=' + value;
|
||||
const int i = find_index(name);
|
||||
if (i >= 0)
|
||||
(*this)[i] = std::move(nv);
|
||||
else
|
||||
push_back(std::move(nv));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Basic exception handling. Allow exception classes for specific errors
|
||||
// to be easily defined, and allow exceptions to be thrown with a consise
|
||||
// syntax that allows stringstream concatenation using <<
|
||||
|
||||
#ifndef OPENVPN_COMMON_EXCEPTION_H
|
||||
#define OPENVPN_COMMON_EXCEPTION_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/stringize.hpp> // for OPENVPN_STRINGIZE
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
#ifdef OPENVPN_DEBUG_EXCEPTION
|
||||
// well-known preprocessor hack to get __FILE__:__LINE__ rendered as a string
|
||||
# define OPENVPN_FILE_LINE "/" __FILE__ ":" OPENVPN_STRINGIZE(__LINE__)
|
||||
#else
|
||||
# define OPENVPN_FILE_LINE
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// string exception class, where the exception is described by a std::string
|
||||
class Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::string& err) noexcept : err_(err) {}
|
||||
Exception(std::string&& err) noexcept : err_(std::move(err)) {}
|
||||
virtual const char* what() const throw() { return err_.c_str(); }
|
||||
const std::string& err() const noexcept { return err_; }
|
||||
virtual ~Exception() throw() {}
|
||||
|
||||
void add_label(const std::string& label)
|
||||
{
|
||||
err_ = label + ": " + err_;
|
||||
}
|
||||
|
||||
void remove_label(const std::string& label)
|
||||
{
|
||||
const std::string head = label + ": ";
|
||||
if (string::starts_with(err_, head))
|
||||
err_ = err_.substr(head.length());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string err_;
|
||||
};
|
||||
|
||||
// define a simple custom exception class with no extra info
|
||||
# define OPENVPN_SIMPLE_EXCEPTION(C) \
|
||||
class C : public std::exception { \
|
||||
public: \
|
||||
virtual const char* what() const throw() { return #C OPENVPN_FILE_LINE; } \
|
||||
}
|
||||
|
||||
// define a simple custom exception class with no extra info that inherits from a custom base
|
||||
# define OPENVPN_SIMPLE_EXCEPTION_INHERIT(B, C) \
|
||||
class C : public B { \
|
||||
public: \
|
||||
C() : B(#C OPENVPN_FILE_LINE) {} \
|
||||
virtual const char* what() const throw() { return #C OPENVPN_FILE_LINE; } \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info
|
||||
# define OPENVPN_EXCEPTION(C) \
|
||||
class C : public openvpn::Exception { \
|
||||
public: \
|
||||
C() : openvpn::Exception(#C OPENVPN_FILE_LINE) {} \
|
||||
C(const std::string err) : openvpn::Exception(#C OPENVPN_FILE_LINE ": " + err) {} \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info, but does not emit a tag
|
||||
# define OPENVPN_UNTAGGED_EXCEPTION(C) \
|
||||
class C : public openvpn::Exception { \
|
||||
public: \
|
||||
C(const std::string err) : openvpn::Exception(err) {} \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info, and inherits from a custom base
|
||||
# define OPENVPN_EXCEPTION_INHERIT(B, C) \
|
||||
class C : public B { \
|
||||
public: \
|
||||
C() : B(#C OPENVPN_FILE_LINE) {} \
|
||||
C(const std::string err) : B(#C OPENVPN_FILE_LINE ": " + err) {} \
|
||||
}
|
||||
|
||||
// define a custom exception class that allows extra info, and inherits from a custom base,
|
||||
// but does not emit a tag
|
||||
# define OPENVPN_UNTAGGED_EXCEPTION_INHERIT(B, C) \
|
||||
class C : public B { \
|
||||
public: \
|
||||
C(const std::string err) : B(err) {} \
|
||||
}
|
||||
|
||||
// throw an Exception with stringstream concatenation allowed
|
||||
# define OPENVPN_THROW_EXCEPTION(stuff) \
|
||||
do { \
|
||||
std::ostringstream _ovpn_exc; \
|
||||
_ovpn_exc << stuff; \
|
||||
throw openvpn::Exception(_ovpn_exc.str()); \
|
||||
} while (0)
|
||||
|
||||
// throw an OPENVPN_EXCEPTION class with stringstream concatenation allowed
|
||||
# define OPENVPN_THROW(exc, stuff) \
|
||||
do { \
|
||||
std::ostringstream _ovpn_exc; \
|
||||
_ovpn_exc << stuff; \
|
||||
throw exc(_ovpn_exc.str()); \
|
||||
} while (0)
|
||||
|
||||
// properly rethrow an exception that might be derived from Exception
|
||||
inline void throw_ref(const std::exception& e)
|
||||
{
|
||||
const Exception* ex = dynamic_cast<const Exception*>(&e);
|
||||
if (ex)
|
||||
throw *ex;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_EXCEPTION_H
|
||||
@@ -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 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_COMMON_EXTERN_H
|
||||
#define OPENVPN_COMMON_EXTERN_H
|
||||
|
||||
#ifndef OPENVPN_EXTERN
|
||||
// Remember that OPENVPN_EXTERN was not defined since something like
|
||||
// #if OPENVPN_EXTERN == extern or OPENVPN_EXTERN == "" is not allowed
|
||||
// in C/C++ preprocessor
|
||||
#define OPENVPN_NO_EXTERN
|
||||
#define OPENVPN_EXTERN
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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 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_COMMON_FFS_H
|
||||
#define OPENVPN_COMMON_FFS_H
|
||||
|
||||
// find_first_set: find the one-based position of the first 1 bit in
|
||||
// a word (scanning from least significant bit to most significant)
|
||||
|
||||
// find_last_set: find the one-based position of the last 1 bit in
|
||||
// a word (scanning from most significant bit to least significant)
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
template <typename T>
|
||||
inline constexpr int n_bits_type()
|
||||
{
|
||||
return sizeof(T) * 8;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr int n_bits_type(const T& v)
|
||||
{
|
||||
return sizeof(v) * 8;
|
||||
}
|
||||
|
||||
inline int find_first_set(const unsigned int v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return __builtin_ffs(v);
|
||||
}
|
||||
|
||||
inline int find_first_set(const int v)
|
||||
{
|
||||
return find_first_set(static_cast<unsigned int>(v));
|
||||
}
|
||||
|
||||
inline int find_last_set(const unsigned int v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return n_bits_type(v) - __builtin_clz(v);
|
||||
}
|
||||
|
||||
inline int find_last_set(const int v)
|
||||
{
|
||||
return find_last_set(static_cast<unsigned int>(v));
|
||||
}
|
||||
|
||||
inline int find_first_set(const unsigned long v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return __builtin_ffsl(v);
|
||||
}
|
||||
|
||||
inline int find_first_set(const long v)
|
||||
{
|
||||
return find_first_set(static_cast<unsigned long>(v));
|
||||
}
|
||||
|
||||
inline int find_last_set(const unsigned long v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return n_bits_type(v) - __builtin_clzl(v);
|
||||
}
|
||||
|
||||
inline int find_last_set(const long v)
|
||||
{
|
||||
return find_last_set(static_cast<unsigned long>(v));
|
||||
}
|
||||
|
||||
inline int find_first_set(const unsigned long long v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return __builtin_ffsll(v);
|
||||
}
|
||||
|
||||
inline int find_first_set(const long long v)
|
||||
{
|
||||
return find_first_set(static_cast<unsigned long long>(v));
|
||||
}
|
||||
|
||||
inline int find_last_set(const unsigned long long v)
|
||||
{
|
||||
if (!v)
|
||||
return 0;
|
||||
return n_bits_type(v) - __builtin_clzll(v);
|
||||
}
|
||||
|
||||
inline int find_last_set(const long long v)
|
||||
{
|
||||
return find_last_set(static_cast<unsigned long long>(v));
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
inline int find_first_set(unsigned int x)
|
||||
{
|
||||
if (!x)
|
||||
return 0;
|
||||
unsigned int r = 0;
|
||||
_BitScanForward((unsigned long *)&r, x);
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
inline int find_last_set(unsigned int x)
|
||||
{
|
||||
if (!x)
|
||||
return 0;
|
||||
unsigned int r = 0;
|
||||
_BitScanReverse((unsigned long *)&r, x);
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
#else
|
||||
#error no find_first_set / find_last_set implementation for this platform
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline bool is_pow2(const T v)
|
||||
{
|
||||
return v && find_first_set(v) == find_last_set(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline int log2(const T v)
|
||||
{
|
||||
return find_last_set(v) - 1;
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_FFS_H
|
||||
@@ -0,0 +1,213 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Basic file-handling methods.
|
||||
|
||||
#ifndef OPENVPN_COMMON_FILE_H
|
||||
#define OPENVPN_COMMON_FILE_H
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdint> // for std::uint64_t
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <openvpn/win/unicode.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_UNTAGGED_EXCEPTION(file_exception);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, open_file_error);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, file_too_large);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, file_is_binary);
|
||||
OPENVPN_UNTAGGED_EXCEPTION_INHERIT(file_exception, file_not_utf8);
|
||||
|
||||
// Read text from file via stream approach that doesn't require that we
|
||||
// establish the length of the file in advance.
|
||||
inline std::string read_text_simple(const std::string& filename)
|
||||
{
|
||||
std::ifstream ifs(filename.c_str());
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for read: " << filename);
|
||||
const std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot read: " << filename);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Read a file (may be text or binary).
|
||||
inline BufferPtr read_binary(const std::string& filename,
|
||||
const std::uint64_t max_size = 0,
|
||||
const unsigned int buffer_flags = 0)
|
||||
{
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
Win::UTF16 filenamew(Win::utf16(filename));
|
||||
std::ifstream ifs(filenamew.get(), std::ios::binary);
|
||||
#else
|
||||
std::ifstream ifs(filename.c_str(), std::ios::binary);
|
||||
#endif
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for read: " << filename);
|
||||
|
||||
// get length of file
|
||||
ifs.seekg (0, std::ios::end);
|
||||
const std::streamsize length = ifs.tellg();
|
||||
if (max_size && std::uint64_t(length) > max_size)
|
||||
OPENVPN_THROW(file_too_large, "file too large [" << length << '/' << max_size << "]: " << filename);
|
||||
ifs.seekg (0, std::ios::beg);
|
||||
|
||||
// allocate buffer
|
||||
BufferPtr b = new BufferAllocated(size_t(length), buffer_flags | BufferAllocated::ARRAY);
|
||||
|
||||
// read data
|
||||
ifs.read((char *)b->data(), length);
|
||||
|
||||
// check for errors
|
||||
if (ifs.gcount() != length)
|
||||
OPENVPN_THROW(open_file_error, "read length inconsistency: " << filename);
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot read: " << filename);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
// Read a file (may be text or binary) without seeking to determine
|
||||
// its length.
|
||||
inline BufferPtr read_binary_linear(const std::string& filename,
|
||||
const std::uint64_t max_size = 0,
|
||||
const size_t block_size = 1024)
|
||||
{
|
||||
std::ifstream ifs(filename.c_str(), std::ios::binary);
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for read: " << filename);
|
||||
|
||||
BufferList buflist;
|
||||
std::streamsize total_size = 0;
|
||||
while (true)
|
||||
{
|
||||
BufferPtr b = new BufferAllocated(block_size, 0);
|
||||
ifs.read((char *)b->data(), b->remaining());
|
||||
const std::streamsize size = ifs.gcount();
|
||||
if (size)
|
||||
{
|
||||
b->set_size(size);
|
||||
total_size += size;
|
||||
if (max_size && std::uint64_t(total_size) > max_size)
|
||||
OPENVPN_THROW(file_too_large, "file too large [" << total_size << '/' << max_size << "]: " << filename);
|
||||
buflist.push_back(std::move(b));
|
||||
}
|
||||
if (ifs.eof())
|
||||
break;
|
||||
if (!ifs)
|
||||
OPENVPN_THROW(open_file_error, "cannot read: " << filename);
|
||||
}
|
||||
return buflist.join();
|
||||
}
|
||||
|
||||
// Read a text file as a std::string, throw error if file is binary
|
||||
inline std::string read_text(const std::string& filename, const std::uint64_t max_size = 0)
|
||||
{
|
||||
BufferPtr bp = read_binary(filename, max_size);
|
||||
if (bp->contains_null())
|
||||
OPENVPN_THROW(file_is_binary, "file is binary: " << filename);
|
||||
return std::string((const char *)bp->c_data(), bp->size());
|
||||
}
|
||||
|
||||
// Read a UTF-8 file as a std::string, throw errors if file is binary or malformed UTF-8
|
||||
inline std::string read_text_utf8(const std::string& filename, const std::uint64_t max_size = 0)
|
||||
{
|
||||
BufferPtr bp = read_binary(filename, max_size);
|
||||
|
||||
// check if binary
|
||||
if (bp->contains_null())
|
||||
OPENVPN_THROW(file_is_binary, "file is binary: " << filename);
|
||||
|
||||
// remove Windows UTF-8 BOM if present
|
||||
if (bp->size() >= 3)
|
||||
{
|
||||
const unsigned char *data = bp->c_data();
|
||||
if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF)
|
||||
bp->advance(3);
|
||||
}
|
||||
|
||||
// verify that file is valid UTF-8
|
||||
if (!Unicode::is_valid_utf8_uchar_buf(bp->c_data(), bp->size()))
|
||||
OPENVPN_THROW(file_not_utf8, "file is not UTF8: " << filename);
|
||||
|
||||
return std::string((const char *)bp->c_data(), bp->size());
|
||||
}
|
||||
|
||||
// Read multi-line string from stdin
|
||||
inline std::string read_stdin()
|
||||
{
|
||||
std::string ret;
|
||||
std::string line;
|
||||
while (std::getline(std::cin, line))
|
||||
{
|
||||
ret += line;
|
||||
ret += '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write binary buffer to file
|
||||
inline void write_binary(const std::string& filename, const Buffer& buf)
|
||||
{
|
||||
std::ofstream ofs(filename.c_str(), std::ios::binary);
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for write: " << filename);
|
||||
ofs.write((const char *)buf.c_data(), buf.size());
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot write: " << filename);
|
||||
}
|
||||
|
||||
// Write binary buffer list to file
|
||||
template <typename BUFLIST>
|
||||
inline void write_binary_list(const std::string& filename, const BUFLIST& buflist)
|
||||
{
|
||||
std::ofstream ofs(filename.c_str(), std::ios::binary);
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot open for write: " << filename);
|
||||
for (auto &buf : buflist)
|
||||
{
|
||||
ofs.write((const char *)buf->c_data(), buf->size());
|
||||
if (!ofs)
|
||||
OPENVPN_THROW(open_file_error, "cannot write: " << filename);
|
||||
}
|
||||
}
|
||||
|
||||
// Write std::string to file
|
||||
inline void write_string(const std::string& filename, const std::string& str)
|
||||
{
|
||||
BufferPtr buf = buf_from_string(str);
|
||||
write_binary(filename, *buf);
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_FILE_H
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Atomic file-handling methods.
|
||||
|
||||
#ifndef OPENVPN_COMMON_FILEATOMIC_H
|
||||
#define OPENVPN_COMMON_FILEATOMIC_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
#error atomic file methods not supported on Windows
|
||||
#endif
|
||||
|
||||
#include <stdio.h> // for rename()
|
||||
#include <unistd.h> // for unlink()
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/fileunix.hpp>
|
||||
#include <openvpn/common/path.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// Atomically write binary buffer to file (relies on
|
||||
// the atomicity of rename())
|
||||
inline void write_binary_atomic(const std::string& fn,
|
||||
const std::string& tmpdir,
|
||||
const mode_t mode,
|
||||
const std::uint64_t mtime_ns, // set explicit modification-time in nanoseconds since epoch, or 0 to defer to system
|
||||
const ConstBuffer& buf,
|
||||
RandomAPI& rng)
|
||||
{
|
||||
// generate temporary filename
|
||||
unsigned char data[16];
|
||||
rng.rand_fill(data);
|
||||
const std::string tfn = path::join(tmpdir, '.' + path::basename(fn).substr(0, 64) + '.' + render_hex(data, sizeof(data)));
|
||||
|
||||
// write to temporary file
|
||||
write_binary_unix(tfn, mode, mtime_ns, buf);
|
||||
|
||||
// then move into position
|
||||
if (::rename(tfn.c_str(), fn.c_str()) == -1)
|
||||
{
|
||||
const int eno = errno;
|
||||
::unlink(tfn.c_str()); // move failed, so delete the temporary file
|
||||
OPENVPN_THROW(file_unix_error, "error moving '" << tfn << "' -> '" << fn << "' : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_binary_atomic(const std::string& fn,
|
||||
const std::string& tmpdir,
|
||||
const mode_t mode,
|
||||
const std::uint64_t mtime_ns,
|
||||
const Buffer& buf,
|
||||
RandomAPI& rng)
|
||||
{
|
||||
return write_binary_atomic(fn, tmpdir, mode, mtime_ns, const_buffer_ref(buf), rng);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Unix file read/write
|
||||
|
||||
#ifndef OPENVPN_COMMON_FILEUNIX_H
|
||||
#define OPENVPN_COMMON_FILEUNIX_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
#error unix file methods not supported on Windows
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h> // for lseek
|
||||
#include <sys/types.h> // for lseek, open
|
||||
#include <sys/stat.h> // for open
|
||||
#include <fcntl.h> // for open
|
||||
#include <cstdint>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/write.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
#include <openvpn/common/modstat.hpp>
|
||||
#include <openvpn/buffer/bufread.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
OPENVPN_EXCEPTION(file_unix_error);
|
||||
|
||||
// write binary buffer to file
|
||||
inline void write_binary_unix(const std::string& fn,
|
||||
const mode_t mode,
|
||||
const std::uint64_t mtime_ns, // set explicit modification-time in nanoseconds since epoch, or 0 to defer to system
|
||||
const void *buf,
|
||||
const size_t size)
|
||||
{
|
||||
// open
|
||||
ScopedFD fd(::open(fn.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, mode));
|
||||
if (!fd.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
throw file_unix_error(fn + " : open for write : " + strerror_str(eno));
|
||||
}
|
||||
|
||||
// write
|
||||
if (size)
|
||||
{
|
||||
const ssize_t len = write_retry(fd(), buf, size);
|
||||
if (len != size)
|
||||
throw file_unix_error(fn + " : incomplete write");
|
||||
}
|
||||
|
||||
// explicit modification time
|
||||
if (mtime_ns)
|
||||
update_file_mod_time_nanoseconds(fd(), mtime_ns);
|
||||
|
||||
// close
|
||||
{
|
||||
const int eno = fd.close_with_errno();
|
||||
if (eno)
|
||||
throw file_unix_error(fn + " : close for write : " + strerror_str(eno));
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_binary_unix(const std::string& fn,
|
||||
const mode_t mode,
|
||||
const std::uint64_t mtime_ns,
|
||||
const Buffer& buf)
|
||||
{
|
||||
write_binary_unix(fn, mode, mtime_ns, buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
inline void write_binary_unix(const std::string& fn,
|
||||
const mode_t mode,
|
||||
const std::uint64_t mtime_ns,
|
||||
const ConstBuffer& buf)
|
||||
{
|
||||
write_binary_unix(fn, mode, mtime_ns, buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
inline void write_text_unix(const std::string& fn,
|
||||
const mode_t mode,
|
||||
const std::uint64_t mtime_ns,
|
||||
const std::string& content)
|
||||
{
|
||||
write_binary_unix(fn, mode, mtime_ns, content.c_str(), content.length());
|
||||
}
|
||||
|
||||
enum { // MUST be distinct from BufferAllocated flags
|
||||
NULL_ON_ENOENT = (1<<8),
|
||||
};
|
||||
inline BufferPtr read_binary_unix(const std::string& fn,
|
||||
const std::uint64_t max_size = 0,
|
||||
const unsigned int buffer_flags = 0)
|
||||
{
|
||||
// open
|
||||
ScopedFD fd(::open(fn.c_str(), O_RDONLY|O_CLOEXEC));
|
||||
if (!fd.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
if ((buffer_flags & NULL_ON_ENOENT) && eno == ENOENT)
|
||||
return BufferPtr();
|
||||
throw file_unix_error(fn + " : open for read : " + strerror_str(eno));
|
||||
}
|
||||
|
||||
// get file length
|
||||
const off_t length = ::lseek(fd(), 0, SEEK_END);
|
||||
if (length < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
throw file_unix_error(fn + " : seek end error : " + strerror_str(eno));
|
||||
}
|
||||
if (::lseek(fd(), 0, SEEK_SET) != 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
throw file_unix_error(fn + " : seek begin error : " + strerror_str(eno));
|
||||
}
|
||||
|
||||
// maximum size exceeded?
|
||||
if (max_size && std::uint64_t(length) > max_size)
|
||||
throw file_unix_error(fn + " : file too large [" + std::to_string(length) + '/' + std::to_string(max_size) + ']');
|
||||
|
||||
// allocate buffer
|
||||
BufferPtr bp = new BufferAllocated(size_t(length), buffer_flags);
|
||||
|
||||
// read file content into buffer
|
||||
while (buf_read(fd(), *bp, fn))
|
||||
;
|
||||
|
||||
// check for close error
|
||||
{
|
||||
const int eno = fd.close_with_errno();
|
||||
if (eno)
|
||||
throw file_unix_error(fn + " : close for read : " + strerror_str(eno));
|
||||
}
|
||||
|
||||
return bp;
|
||||
}
|
||||
|
||||
inline int read_binary_unix_fast(const std::string& fn,
|
||||
Buffer& out)
|
||||
{
|
||||
ScopedFD fd(::open(fn.c_str(), O_RDONLY|O_CLOEXEC));
|
||||
if (!fd.defined())
|
||||
return errno;
|
||||
const ssize_t status = ::read(fd(), out.data_end(), out.remaining(0));
|
||||
if (status < 0)
|
||||
return errno;
|
||||
out.inc_size(status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline std::string read_text_unix(const std::string& filename,
|
||||
const std::uint64_t max_size = 0,
|
||||
const unsigned int buffer_flags = 0)
|
||||
{
|
||||
BufferPtr bp = read_binary_unix(filename, max_size, buffer_flags);
|
||||
if (bp)
|
||||
return buf_to_string(*bp);
|
||||
else
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,337 @@
|
||||
// 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 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_COMMON_FORMAT_H
|
||||
#define OPENVPN_COMMON_FORMAT_H
|
||||
|
||||
#include <cstddef> // for std::nullptr_t
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Concatenate arguments into a string:
|
||||
// print(args...) -- concatenate
|
||||
// prints(args...) -- concatenate but delimit args with space
|
||||
// printd(char delim, args...) -- concatenate but delimit args with delim
|
||||
|
||||
namespace print_detail {
|
||||
template<typename T>
|
||||
inline void print(std::ostream& os, char delim, const T& first)
|
||||
{
|
||||
os << first;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline void print(std::ostream& os, char delim, const T& first, Args... args)
|
||||
{
|
||||
os << first;
|
||||
if (delim)
|
||||
os << delim;
|
||||
print(os, delim, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string printd(char delim, Args... args)
|
||||
{
|
||||
std::ostringstream os;
|
||||
print_detail::print(os, delim, args...);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string print(Args... args)
|
||||
{
|
||||
return printd(0, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string prints(Args... args)
|
||||
{
|
||||
return printd(' ', args...);
|
||||
}
|
||||
|
||||
// String formatting similar to sprintf.
|
||||
// %s formats any argument regardless of type.
|
||||
// %r formats any argument regardless of type and single-quotes it.
|
||||
// %R formats any argument regardless of type and double-quotes it.
|
||||
// %% formats '%'
|
||||
// printfmt(<format_string>, args...)
|
||||
|
||||
namespace print_formatted_detail {
|
||||
template<typename T>
|
||||
class Output {};
|
||||
|
||||
template<>
|
||||
class Output<std::string>
|
||||
{
|
||||
public:
|
||||
Output(const size_t reserve)
|
||||
{
|
||||
if (reserve)
|
||||
str_.reserve(reserve);
|
||||
}
|
||||
|
||||
// numeric types
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
|
||||
void append(T value)
|
||||
{
|
||||
str_ += openvpn::to_string(value);
|
||||
}
|
||||
|
||||
// non-numeric types not specialized below
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
|
||||
void append(const T& value)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << value;
|
||||
str_ += os.str();
|
||||
}
|
||||
|
||||
// specialization for std::string
|
||||
void append(const std::string& value)
|
||||
{
|
||||
str_ += value;
|
||||
}
|
||||
|
||||
// specialization for const char *
|
||||
void append(const char *value)
|
||||
{
|
||||
if (value)
|
||||
str_ += value;
|
||||
}
|
||||
|
||||
// specialization for char *
|
||||
void append(char *value)
|
||||
{
|
||||
if (value)
|
||||
str_ += value;
|
||||
}
|
||||
|
||||
// specialization for char
|
||||
void append(const char c)
|
||||
{
|
||||
str_ += c;
|
||||
}
|
||||
|
||||
// specialization for bool
|
||||
void append(const bool value)
|
||||
{
|
||||
str_ += value ? "true" : "false";
|
||||
}
|
||||
|
||||
// specialization for nullptr
|
||||
void append(std::nullptr_t)
|
||||
{
|
||||
str_ += "nullptr";
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
return std::move(str_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string str_;
|
||||
};
|
||||
|
||||
template<>
|
||||
class Output<std::ostringstream>
|
||||
{
|
||||
public:
|
||||
Output(const size_t reserve)
|
||||
{
|
||||
// fixme -- figure out how to reserve space in std::ostringstream
|
||||
}
|
||||
|
||||
// general types
|
||||
template <typename T>
|
||||
void append(const T& value)
|
||||
{
|
||||
os_ << value;
|
||||
}
|
||||
|
||||
// specialization for const char *
|
||||
void append(const char *value)
|
||||
{
|
||||
if (value)
|
||||
os_ << value;
|
||||
}
|
||||
|
||||
// specialization for char *
|
||||
void append(char *value)
|
||||
{
|
||||
if (value)
|
||||
os_ << value;
|
||||
}
|
||||
|
||||
// specialization for bool
|
||||
void append(const bool value)
|
||||
{
|
||||
if (value)
|
||||
os_ << "true";
|
||||
else
|
||||
os_ << "false";
|
||||
}
|
||||
|
||||
// specialization for nullptr
|
||||
void append(std::nullptr_t)
|
||||
{
|
||||
os_ << "nullptr";
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
return os_.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream os_;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename OUTPUT>
|
||||
class PrintFormatted
|
||||
{
|
||||
public:
|
||||
PrintFormatted(const std::string& fmt_arg, const size_t reserve)
|
||||
: fmt(fmt_arg),
|
||||
fi(fmt.begin()),
|
||||
out(reserve),
|
||||
pct(false)
|
||||
{
|
||||
}
|
||||
|
||||
void process()
|
||||
{
|
||||
process_finish();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void process(const T& last)
|
||||
{
|
||||
process_arg(last);
|
||||
process_finish();
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void process(const T& first, Args... args)
|
||||
{
|
||||
process_arg(first);
|
||||
process(args...);
|
||||
}
|
||||
|
||||
std::string str()
|
||||
{
|
||||
return out.str();
|
||||
}
|
||||
|
||||
private:
|
||||
PrintFormatted(const PrintFormatted&) = delete;
|
||||
PrintFormatted& operator=(const PrintFormatted&) = delete;
|
||||
|
||||
template<typename T>
|
||||
bool process_arg(const T& arg)
|
||||
{
|
||||
while (fi != fmt.end())
|
||||
{
|
||||
const char c = *fi++;
|
||||
if (pct)
|
||||
{
|
||||
pct = false;
|
||||
const int quote = quote_delim(c);
|
||||
if (quote >= 0)
|
||||
{
|
||||
if (quote)
|
||||
out.append((char)quote);
|
||||
out.append(arg);
|
||||
if (quote)
|
||||
out.append((char)quote);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
out.append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '%')
|
||||
pct = true;
|
||||
else
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void process_finish()
|
||||
{
|
||||
// '?' printed for %s operators that don't match an argument
|
||||
while (process_arg("?"))
|
||||
;
|
||||
}
|
||||
|
||||
static int quote_delim(const char fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case 's':
|
||||
return 0;
|
||||
case 'r':
|
||||
return '\'';
|
||||
case 'R':
|
||||
return '\"';
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& fmt;
|
||||
std::string::const_iterator fi;
|
||||
print_formatted_detail::Output<OUTPUT> out;
|
||||
bool pct;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string printfmt(const std::string& fmt, Args... args)
|
||||
{
|
||||
#ifdef OPENVPN_PLATFORM_ANDROID
|
||||
PrintFormatted<std::ostringstream> pf(fmt, 256);
|
||||
#else
|
||||
PrintFormatted<std::string> pf(fmt, 256);
|
||||
#endif
|
||||
pf.process(args...);
|
||||
return pf.str();
|
||||
}
|
||||
|
||||
# define OPENVPN_FMT(...) OPENVPN_LOG_STRING(printfmt(__VA_ARGS__))
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,257 @@
|
||||
// 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 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/>.
|
||||
|
||||
// High-performance functor with move-only semantics.
|
||||
|
||||
#ifndef OPENVPN_COMMON_FUNCTION_H
|
||||
#define OPENVPN_COMMON_FUNCTION_H
|
||||
|
||||
#include <cstddef> // for std::size_t
|
||||
#include <utility> // for std::move
|
||||
#include <type_traits>
|
||||
#include <new>
|
||||
|
||||
namespace openvpn {
|
||||
// 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;
|
||||
|
||||
template <typename R, typename... A, std::size_t N, bool INTERN_ONLY>
|
||||
class Function<R(A...), N, INTERN_ONLY>
|
||||
{
|
||||
public:
|
||||
Function() noexcept
|
||||
{
|
||||
methods = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Function(T&& functor) noexcept
|
||||
{
|
||||
construct(std::move(functor));
|
||||
}
|
||||
|
||||
Function(Function&& other) noexcept
|
||||
{
|
||||
methods = other.methods;
|
||||
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()
|
||||
{
|
||||
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) const
|
||||
{
|
||||
return methods->invoke(data, std::forward<A>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return methods != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef _MSC_VER
|
||||
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));
|
||||
}
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
static constexpr bool is_intern()
|
||||
{
|
||||
return sizeof(Intern<T>) <= sizeof(data);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<is_intern<T>(), int>::type = 0>
|
||||
void construct(T&& functor) noexcept
|
||||
{
|
||||
// store functor internally (in data)
|
||||
setup_methods_intern<T>();
|
||||
new (data) Intern<T>(std::move(functor));
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<!is_intern<T>(), int>::type = 0>
|
||||
void construct(T&& functor) noexcept
|
||||
{
|
||||
static_assert(!INTERN_ONLY, "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[]");
|
||||
|
||||
// store functor externally (using new)
|
||||
setup_methods_extern<T>();
|
||||
new (data) Extern<T>(std::move(functor));
|
||||
}
|
||||
#endif
|
||||
|
||||
struct Methods
|
||||
{
|
||||
R (*invoke)(void *, A&&...);
|
||||
void (*move)(void *, void *);
|
||||
void (*destruct)(void *);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void setup_methods_intern()
|
||||
{
|
||||
static const struct Methods m = {
|
||||
&Intern<T>::invoke,
|
||||
&Intern<T>::move,
|
||||
&Intern<T>::destruct,
|
||||
};
|
||||
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>
|
||||
class Intern
|
||||
{
|
||||
public:
|
||||
Intern(T&& functor) noexcept
|
||||
: functor_(std::move(functor))
|
||||
{
|
||||
}
|
||||
|
||||
static R invoke(void* ptr, A&&... args)
|
||||
{
|
||||
Intern* self = reinterpret_cast<Intern*>(ptr);
|
||||
return self->functor_(std::forward<A>(args)...);
|
||||
}
|
||||
|
||||
static void move(void *dest, void *src)
|
||||
{
|
||||
Intern* s = reinterpret_cast<Intern*>(src);
|
||||
new (dest) Intern(std::move(*s));
|
||||
}
|
||||
|
||||
static void destruct(void *ptr)
|
||||
{
|
||||
Intern* self = reinterpret_cast<Intern*>(ptr);
|
||||
self->~Intern();
|
||||
}
|
||||
|
||||
private:
|
||||
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 *>(ptr);
|
||||
return (*self->functor_)(std::forward<A>(args)...);
|
||||
}
|
||||
|
||||
static void move(void *dest, void *src)
|
||||
{
|
||||
Extern* d = reinterpret_cast<Extern*>(dest);
|
||||
Extern* s = reinterpret_cast<Extern*>(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*>(ptr);
|
||||
delete self->functor_;
|
||||
}
|
||||
|
||||
private:
|
||||
T* functor_;
|
||||
};
|
||||
|
||||
const Methods* methods;
|
||||
mutable void* data[N];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 1987, 1993, 1994, 1996
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef OPENVPN_COMMON_GETOPT_H
|
||||
#define OPENVPN_COMMON_GETOPT_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <getopt.h>
|
||||
#else
|
||||
|
||||
#include <cstring> // for std::strlen, std::strchr, std::strncmp
|
||||
|
||||
#define GETOPT_BADCH (int)'?'
|
||||
#define GETOPT_BADARG (int)':'
|
||||
#define GETOPT_EMSG ""
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(getopt_assert);
|
||||
OPENVPN_EXCEPTION(getopt_exception);
|
||||
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = 0; /* character checked for validity */
|
||||
int optreset = 0; /* reset getopt */
|
||||
char *optarg = nullptr; /* argument associated with option */
|
||||
|
||||
struct option
|
||||
{
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
enum {
|
||||
no_argument=0,
|
||||
required_argument=1,
|
||||
optional_argument=2
|
||||
};
|
||||
|
||||
namespace getopt_private {
|
||||
inline void error(const char *prefix, int arg)
|
||||
{
|
||||
std::string err = prefix;
|
||||
err += " -- ";
|
||||
err += (char)arg;
|
||||
throw getopt_exception(err);
|
||||
}
|
||||
|
||||
inline void error(const char *prefix, const char *arg)
|
||||
{
|
||||
std::string err = prefix;
|
||||
err += " -- ";
|
||||
err += arg;
|
||||
throw getopt_exception(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
inline int getopt_internal(int nargc, char * const *nargv, const char *ostr)
|
||||
{
|
||||
static char *place = GETOPT_EMSG; /* option letter processing */
|
||||
const char *oli; /* option letter list index */
|
||||
|
||||
if (!nargv || !ostr)
|
||||
throw getopt_assert();
|
||||
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
||||
place = GETOPT_EMSG;
|
||||
return (-1);
|
||||
}
|
||||
if (place[1] && *++place == '-') { /* found "--" */
|
||||
/* ++optind; */
|
||||
place = GETOPT_EMSG;
|
||||
return (-2);
|
||||
}
|
||||
} /* option letter okay? */
|
||||
if ((optopt = (int)*place++) == (int)':' ||
|
||||
!(oli = std::strchr(ostr, optopt))) {
|
||||
/*
|
||||
* if the user didn't specify '-' as an option,
|
||||
* assume it means -1.
|
||||
*/
|
||||
if (optopt == (int)'-')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (opterr && *ostr != ':')
|
||||
getopt_private::error("illegal option", optopt);
|
||||
return (GETOPT_BADCH);
|
||||
}
|
||||
if (*++oli != ':') { /* don't need argument */
|
||||
optarg = nullptr;
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* need an argument */
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (nargc <= ++optind) { /* no arg */
|
||||
place = GETOPT_EMSG;
|
||||
if ((opterr) && (*ostr != ':'))
|
||||
getopt_private::error("option requires an argument", optopt);
|
||||
return (GETOPT_BADARG);
|
||||
} else /* white space */
|
||||
optarg = nargv[optind];
|
||||
place = GETOPT_EMSG;
|
||||
++optind;
|
||||
}
|
||||
return (optopt); /* dump back option letter */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
inline int getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *index)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!nargv || !options || !long_options)
|
||||
throw getopt_assert();
|
||||
|
||||
if ((retval = getopt_private::getopt_internal(nargc, nargv, options)) == -2) {
|
||||
char *current_argv = nargv[optind++] + 2;
|
||||
char *has_equal;
|
||||
int i;
|
||||
int current_argv_len;
|
||||
int match = -1;
|
||||
|
||||
if (*current_argv == '\0')
|
||||
return(-1);
|
||||
if ((has_equal = std::strchr(current_argv, '=')) != nullptr) {
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = std::strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
if (std::strncmp(current_argv, long_options[i].name, current_argv_len))
|
||||
continue;
|
||||
|
||||
if (std::strlen(long_options[i].name) == (unsigned)current_argv_len) {
|
||||
match = i;
|
||||
break;
|
||||
}
|
||||
if (match == -1)
|
||||
match = i;
|
||||
}
|
||||
if (match != -1) {
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == nullptr)) {
|
||||
/*
|
||||
* Missing argument, leading :
|
||||
* indicates no error should be generated
|
||||
*/
|
||||
if ((opterr) && (*options != ':'))
|
||||
getopt_private::error("option requires an argument", current_argv);
|
||||
return (GETOPT_BADARG);
|
||||
}
|
||||
} else { /* No matching argument */
|
||||
if ((opterr) && (*options != ':'))
|
||||
getopt_private::error("illegal option", current_argv);
|
||||
return (GETOPT_BADCH);
|
||||
}
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
retval = 0;
|
||||
} else
|
||||
retval = long_options[match].val;
|
||||
if (index)
|
||||
*index = match;
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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 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_COMMON_GETPW_H
|
||||
#define OPENVPN_COMMON_GETPW_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline std::string get_password(const char *prompt)
|
||||
{
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
char *ret = getpass(prompt);
|
||||
return ret;
|
||||
#else
|
||||
throw Exception("get_password not implemented yet for Windows");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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 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_COMMON_GLOB_H
|
||||
#define OPENVPN_COMMON_GLOB_H
|
||||
|
||||
#include <glob.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace openvpn {
|
||||
class Glob
|
||||
{
|
||||
public:
|
||||
Glob(const std::string& pattern, const int flags)
|
||||
{
|
||||
reset();
|
||||
status_ = ::glob(pattern.c_str(), flags, nullptr, &glob_);
|
||||
}
|
||||
|
||||
int status() const
|
||||
{
|
||||
return status_;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return glob_.gl_pathc;
|
||||
}
|
||||
|
||||
const char* operator[](const size_t i) const
|
||||
{
|
||||
return glob_.gl_pathv[i];
|
||||
}
|
||||
|
||||
~Glob()
|
||||
{
|
||||
::globfree(&glob_);
|
||||
}
|
||||
|
||||
private:
|
||||
void reset()
|
||||
{
|
||||
std::memset(&glob_, 0, sizeof(glob_));
|
||||
status_ = 0;
|
||||
}
|
||||
|
||||
Glob(const Glob&) = delete;
|
||||
Glob& operator=(const Glob&) = delete;
|
||||
|
||||
::glob_t glob_;
|
||||
int status_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,192 @@
|
||||
// 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 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_COMMON_HASH_H
|
||||
#define OPENVPN_COMMON_HASH_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint> // for std::uint32_t, uint64_t
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
|
||||
#define OPENVPN_HASH_METHOD(T, meth) \
|
||||
namespace std { \
|
||||
template <> \
|
||||
struct hash<T> \
|
||||
{ \
|
||||
inline std::size_t operator()(const T& obj) const \
|
||||
{ \
|
||||
return obj.meth(); \
|
||||
} \
|
||||
}; \
|
||||
}
|
||||
|
||||
#ifdef HAVE_CITYHASH
|
||||
|
||||
#ifdef OPENVPN_HASH128_CRC
|
||||
#include <citycrc.h>
|
||||
#define OPENVPN_HASH128 ::CityHashCrc128WithSeed
|
||||
#else
|
||||
#include <city.h>
|
||||
#define OPENVPN_HASH128 ::CityHash128WithSeed
|
||||
#endif
|
||||
|
||||
#if SIZE_MAX == 0xFFFFFFFF
|
||||
#define HashSizeT Hash32
|
||||
#elif SIZE_MAX == 0xFFFFFFFFFFFFFFFF
|
||||
#define HashSizeT Hash64
|
||||
#else
|
||||
#error "Unrecognized SIZE_MAX"
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class Hash128
|
||||
{
|
||||
public:
|
||||
Hash128() : hashval(0,0) {}
|
||||
|
||||
void operator()(const void *data, const std::size_t size)
|
||||
{
|
||||
hashval = OPENVPN_HASH128((const char *)data, size, hashval);
|
||||
}
|
||||
|
||||
void operator()(const std::string& str)
|
||||
{
|
||||
(*this)(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator()(const T& obj)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value, "Hash128: POD type required");
|
||||
(*this)(&obj, sizeof(obj));
|
||||
}
|
||||
|
||||
std::uint64_t high() const
|
||||
{
|
||||
return hashval.second;
|
||||
}
|
||||
|
||||
std::uint64_t low() const
|
||||
{
|
||||
return hashval.first;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return render_hex_number(high()) + render_hex_number(low());
|
||||
}
|
||||
|
||||
private:
|
||||
uint128 hashval;
|
||||
};
|
||||
|
||||
class Hash64
|
||||
{
|
||||
public:
|
||||
Hash64(const std::uint64_t init_hashval=0)
|
||||
: hashval(init_hashval)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const void *data, const std::size_t size)
|
||||
{
|
||||
hashval = ::CityHash64WithSeed((const char *)data, size, hashval);
|
||||
}
|
||||
|
||||
void operator()(const std::string& str)
|
||||
{
|
||||
(*this)(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator()(const T& obj)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value, "Hash64: POD type required");
|
||||
(*this)(&obj, sizeof(obj));
|
||||
}
|
||||
|
||||
std::uint64_t value() const
|
||||
{
|
||||
return hashval;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return render_hex_number(hashval);
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint64_t hashval;
|
||||
};
|
||||
|
||||
class Hash32
|
||||
{
|
||||
public:
|
||||
Hash32(const std::uint32_t init_hashval=0)
|
||||
: hashval(init_hashval)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const void *data, const std::size_t size)
|
||||
{
|
||||
hashval = hash_combine(::CityHash32((const char *)data, size), hashval);
|
||||
}
|
||||
|
||||
void operator()(const std::string& str)
|
||||
{
|
||||
(*this)(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator()(const T& obj)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value, "Hash64: POD type required");
|
||||
(*this)(&obj, sizeof(obj));
|
||||
}
|
||||
|
||||
std::uint32_t value() const
|
||||
{
|
||||
return hashval;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return render_hex_number(hashval);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::uint32_t hash_combine(const std::uint32_t h1,
|
||||
const std::uint32_t h2)
|
||||
{
|
||||
return h1 ^ (h2 + 0x9e3779b9 + (h1<<6) + (h1>>2));
|
||||
}
|
||||
|
||||
std::uint32_t hashval;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,487 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A collection of functions for rendering and parsing hexadecimal strings
|
||||
|
||||
#ifndef OPENVPN_COMMON_HEXSTR_H
|
||||
#define OPENVPN_COMMON_HEXSTR_H
|
||||
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
/**
|
||||
* Renders an integer value within the hexadecimal range (0-15)
|
||||
* to a hexadecimal character.
|
||||
*
|
||||
* @param c Integer to render as a hexadecimal character.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a char with the hexadecimal representation of
|
||||
* the input value. If the value is out-of-range (outside
|
||||
* of 0-15), it will be replaced with a questionmark (?).
|
||||
*/
|
||||
inline char render_hex_char(const int c, const bool caps=false)
|
||||
{
|
||||
if (c < 10)
|
||||
return '0' + c;
|
||||
else if (c < 16)
|
||||
return (caps ? 'A' : 'a') - 10 + c;
|
||||
else
|
||||
return '?';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a character in the range {0..9,A-F,a-f} to an
|
||||
* integer value. Used to convert hexadecimal character to integer.
|
||||
* Only a single character is parsed by this function.
|
||||
*
|
||||
* @param c Character to be be parsed.
|
||||
*
|
||||
* @return Returns an integer value of the hexadecimal input. If the
|
||||
* input character is invalid, outside of {0..9,A-F,a-f}, it will
|
||||
* return -1.
|
||||
*/
|
||||
inline int parse_hex_char(const char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class which Renders a single byte as hexadecimal
|
||||
*/
|
||||
class RenderHexByte
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes a new object
|
||||
*
|
||||
* @param byte Unsigned char (one byte) to be processed
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*/
|
||||
RenderHexByte(const unsigned char byte, const bool caps=false)
|
||||
{
|
||||
c[0] = render_hex_char(byte >> 4, caps);
|
||||
c[1] = render_hex_char(byte & 0x0F, caps);
|
||||
}
|
||||
|
||||
char char1() const { return c[0]; }
|
||||
char char2() const { return c[1]; }
|
||||
|
||||
/**
|
||||
* Retrieve the hexadecimal representation of the value.
|
||||
* Warning: The result is a non-NULL terminated string.
|
||||
*
|
||||
* @return Returns a non-NULL terminated 2 byte string with the hexadecimal
|
||||
* representation of the initial value. The return value is guaranteed
|
||||
* to always be 2 bytes.
|
||||
*/
|
||||
const char *str2() const { return c; } // Note: length=2, NOT null terminated
|
||||
|
||||
private:
|
||||
char c[2];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Render a byte buffer (unsigned char *) as a hexadecimal string.
|
||||
*
|
||||
* @param data Unsigned char pointer to buffer to render.
|
||||
* @param size size_t of the number of bytes to parse from the buffer.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a std::string of the complete hexadecimal representation
|
||||
*/
|
||||
inline std::string render_hex(const unsigned char *data, size_t size, const bool caps=false)
|
||||
{
|
||||
if (!data)
|
||||
return "NULL";
|
||||
std::string ret;
|
||||
ret.reserve(size*2+1);
|
||||
while (size--)
|
||||
{
|
||||
const RenderHexByte b(*data++, caps);
|
||||
ret += b.char1();
|
||||
ret += b.char2();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a byte buffer (void *) as a hexadecimal string.
|
||||
*
|
||||
* @param data Void pointer to buffer to render.
|
||||
* @param size size_t of the number of bytes to parse from the buffer.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a std::string of the complete hexadecimal representation.
|
||||
*/
|
||||
inline std::string render_hex(const void *data, const size_t size, const bool caps=false)
|
||||
{
|
||||
return render_hex((const unsigned char *)data, size, caps);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Variant of @render_hex(const unsiged char *,...) which adds a
|
||||
* separator between each byte
|
||||
*
|
||||
* @param data Unsigned char pointer to buffer to render.
|
||||
* @param size size_t of the number of bytes to parse from the buffer.
|
||||
* @param sep A single character to use as the separator.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a std::string of the complete hexadecimal representation
|
||||
* with each byte separated by a given character.
|
||||
*/
|
||||
inline std::string render_hex_sep(const unsigned char *data, size_t size, const char sep, const bool caps=false)
|
||||
{
|
||||
if (!data)
|
||||
return "NULL";
|
||||
std::string ret;
|
||||
ret.reserve(size*3);
|
||||
bool prsep = false;
|
||||
while (size--)
|
||||
{
|
||||
if (prsep)
|
||||
ret += sep;
|
||||
const RenderHexByte b(*data++, caps);
|
||||
ret += b.char1();
|
||||
ret += b.char2();
|
||||
prsep = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of @render_hex(const void *,...) which adds a
|
||||
* separator between each byte
|
||||
|
||||
* @param data Void pointer to buffer to render.
|
||||
* @param size size_t of the number of bytes to parse from the buffer.
|
||||
* @param sep A single character to use as the separator.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a std::string of the complete hexadecimal representation
|
||||
* with each byte separated by a given character.
|
||||
*/
|
||||
inline std::string render_hex_sep(const void *data, const size_t size, const char sep, const bool caps=false)
|
||||
{
|
||||
return render_hex_sep((const unsigned char *)data, size, sep, caps);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a std::vector<T> container as a hexadecimal string.
|
||||
* T must be a data type compatible with
|
||||
* RenderHexByte(const unsigned char,...)
|
||||
*
|
||||
* @param data std::vector<T> containing the data to render
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a std::string of the complete hexadecimal representation.
|
||||
*/
|
||||
template <typename V>
|
||||
inline std::string render_hex_generic(const V& data, const bool caps=false)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(data.size()*2+1);
|
||||
for (size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
const RenderHexByte b(data[i], caps);
|
||||
ret += b.char1();
|
||||
ret += b.char2();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders a combined hexadecimal and character dump of a buffer,
|
||||
* with the typical 16 bytes split between hexadecimal and character
|
||||
* separation per line.
|
||||
*
|
||||
* @param data Unsigned char pointer to the buffer to dump.
|
||||
* @param size Size of the buffer to render.
|
||||
*
|
||||
* @return Returns a string containing a preformatted output of the
|
||||
* hexadecimal dump.
|
||||
*/
|
||||
inline std::string dump_hex(const unsigned char *data, size_t size)
|
||||
{
|
||||
if (!data)
|
||||
return "NULL\n";
|
||||
const unsigned int mask = 0x0F; // N bytes per line - 1
|
||||
std::ostringstream os;
|
||||
os << std::hex;
|
||||
std::string chars;
|
||||
size_t i;
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
if (!(i & mask))
|
||||
{
|
||||
if (i)
|
||||
{
|
||||
os << " " << chars << std::endl;
|
||||
chars.clear();
|
||||
}
|
||||
os << std::setfill(' ') << std::setw(8) << i << ":";
|
||||
}
|
||||
const unsigned char c = data[i];
|
||||
os << ' ' << std::setfill('0') << std::setw(2) << (unsigned int)c;
|
||||
if (string::is_printable(c))
|
||||
chars += c;
|
||||
else
|
||||
chars += '.';
|
||||
}
|
||||
if (i)
|
||||
os << string::spaces(2 + (((i-1) & mask) ^ mask) * 3) << chars << std::endl;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a combined hexadecimal and character dump of a buffer,
|
||||
* with the typical 16 bytes split between hexadecimal and character
|
||||
* separation per line.
|
||||
*
|
||||
* @param data Void pointer to the buffer to dump.
|
||||
* @param size Size of the buffer to render.
|
||||
*
|
||||
* @return Returns a string containing a preformatted output of the
|
||||
* hexadecimal dump.
|
||||
*/
|
||||
inline std::string dump_hex(void* data, size_t size)
|
||||
{
|
||||
return dump_hex((const unsigned char *)data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a combined hexadecimal and character dump of a std::string buffer,
|
||||
* with the typical 16 bytes split between hexadecimal and character
|
||||
* separation per line.
|
||||
*
|
||||
* @param data std::string containing the buffer to render
|
||||
*
|
||||
* @return Returns a string containing a preformatted output of the
|
||||
* hexadecimal dump.
|
||||
*/
|
||||
inline std::string dump_hex(const std::string& str)
|
||||
{
|
||||
return dump_hex((const unsigned char *)str.c_str(), str.length());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders a combined hexadecimal and character dump of a std::vector<T>
|
||||
* based buffer, with the typical 16 bytes split between hexadecimal and
|
||||
* character separation per line.
|
||||
*
|
||||
* @param data std::vector<T> containing the buffer to render
|
||||
*
|
||||
* @return Returns a string containing a preformatted output of the
|
||||
* hexadecimal dump.
|
||||
*/
|
||||
template <typename V>
|
||||
inline std::string dump_hex(const V& data)
|
||||
{
|
||||
return dump_hex(data.c_data(), data.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Declaration of a hexadecimal parsing error exception class
|
||||
*/
|
||||
OPENVPN_SIMPLE_EXCEPTION(parse_hex_error);
|
||||
|
||||
|
||||
/**
|
||||
* Parses a std::string containing a hexadecimal value into
|
||||
* a std::vector<T>.
|
||||
*
|
||||
* @param dest std::vector<T> destination buffer to use.
|
||||
* @param str std::string& containing the hexadecimal string to parse.
|
||||
*
|
||||
* @return Returns nothing on success. Will throw a parse_hex_error
|
||||
* exception if the input is invalid/not parseable as a hexadecimal
|
||||
* number.
|
||||
*/
|
||||
template <typename V>
|
||||
inline void parse_hex(V& dest, const std::string& str)
|
||||
{
|
||||
const int len = int(str.length());
|
||||
int i;
|
||||
for (i = 0; i <= len - 2; i += 2)
|
||||
{
|
||||
const int high = parse_hex_char(str[i]);
|
||||
const int low = parse_hex_char(str[i+1]);
|
||||
if (high == -1 || low == -1)
|
||||
throw parse_hex_error();
|
||||
dest.push_back((high<<4) + low);
|
||||
}
|
||||
if (i != len)
|
||||
throw parse_hex_error(); // straggler char
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a char buffer (C string) containing a hexadecimal
|
||||
* string into a templated (T) variable. The input buffer
|
||||
* MUST be NULL terminated.
|
||||
*
|
||||
* WARNING: There are _NO_ overflow checks.
|
||||
*
|
||||
* @param str Char pointer (char *) to the buffer to be parsed.
|
||||
* @param retval Return buffer where the parsed value is stored.
|
||||
*
|
||||
* @return Returns true on successful parsing, otherwise false.
|
||||
*/
|
||||
template <typename T>
|
||||
inline bool parse_hex_number(const char *str, T& retval)
|
||||
{
|
||||
if (!str[0])
|
||||
return false; // empty string
|
||||
size_t i = 0;
|
||||
T ret = T(0);
|
||||
while (true)
|
||||
{
|
||||
const char c = str[i++];
|
||||
const int hd = parse_hex_char(c);
|
||||
if (hd >= 0)
|
||||
{
|
||||
ret *= T(16);
|
||||
ret += T(hd);
|
||||
}
|
||||
else if (!c)
|
||||
{
|
||||
retval = ret;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false; // non-hex-digit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Variant of @parse_hex_number(const char *, ...) which takes a std::string
|
||||
* as the input.
|
||||
*
|
||||
* @param str std::string containing the hexadecimal string to be parsed.
|
||||
* @param retval Return buffer where the parsed value is stored.
|
||||
*
|
||||
* @return Returns true on successful parsing, otherwise false.
|
||||
*/
|
||||
template <typename T>
|
||||
inline bool parse_hex_number(const std::string& str, T& retval)
|
||||
{
|
||||
return parse_hex_number(str.c_str(), retval);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a std::string containing a hexadecimal
|
||||
* string into a templated (T) variable.
|
||||
*
|
||||
* NOTE: Currently doesn't detect overflow
|
||||
*
|
||||
* @param str std::string containing the hexadecimal
|
||||
* string to be parsed.
|
||||
*
|
||||
* @return Returns a template T variable containing the
|
||||
* parsed value on success. Will throw the parse_hex_error
|
||||
* exception on parsing errors.
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
inline T parse_hex_number(const std::string& str)
|
||||
{
|
||||
T ret;
|
||||
if (!parse_hex_number<T>(str.c_str(), ret))
|
||||
throw parse_hex_error();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a templated T variable containing a numeric value
|
||||
* into a std::string containing a hexadecimal representation.
|
||||
*
|
||||
* @param value Numeric (T) value to represent as hexadecimal.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Retuns a std::string containing the hexadecimal
|
||||
* representation on succes. Will throw a parse_hex_error
|
||||
* exception on parsing errors.
|
||||
*/
|
||||
template <typename T>
|
||||
std::string render_hex_number(T value, const bool caps=false)
|
||||
{
|
||||
unsigned char buf[sizeof(T)];
|
||||
for (size_t i = sizeof(T); i --> 0 ;)
|
||||
{
|
||||
buf[i] = value & 0xFF;
|
||||
value >>= 8;
|
||||
}
|
||||
return render_hex(buf, sizeof(T), caps);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders a single byte as a hexadecimal string
|
||||
*
|
||||
* @param value Unsigned char (byte) to be represented as hexadecimal.
|
||||
* @param caps Boolean (default false) which sets the outout to
|
||||
* be either lower case (false) or upper case (true).
|
||||
*
|
||||
* @return Returns a std::string with the hexadecimal representation
|
||||
* of the input value. The result will always contain only
|
||||
* two characters.
|
||||
*/
|
||||
inline std::string render_hex_number(unsigned char uc, const bool caps=false)
|
||||
{
|
||||
RenderHexByte b(uc, caps);
|
||||
return std::string(b.str2(), 2);
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_HEXSTR_H
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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 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_COMMON_HOSTLIST_H
|
||||
#define OPENVPN_COMMON_HOSTLIST_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace HostList {
|
||||
|
||||
struct Host
|
||||
{
|
||||
Host() {}
|
||||
|
||||
Host(const std::string& host_arg, const std::string& port_arg)
|
||||
: host(host_arg),
|
||||
port(port_arg)
|
||||
{
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return !host.empty();
|
||||
}
|
||||
|
||||
void swap(Host& rhs) noexcept
|
||||
{
|
||||
host.swap(rhs.host);
|
||||
port.swap(rhs.port);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
host.clear();
|
||||
port.clear();
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
if (defined())
|
||||
os << '[' << host << "]:" << port;
|
||||
else
|
||||
os << "UNDEF_HOST";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string host;
|
||||
std::string port;
|
||||
};
|
||||
|
||||
class List : public std::vector<Host>
|
||||
{
|
||||
public:
|
||||
List() {}
|
||||
|
||||
List(const OptionList& opt,
|
||||
const std::string& directive,
|
||||
const std::string& default_port)
|
||||
{
|
||||
auto hl = opt.get_index_ptr(directive);
|
||||
if (hl)
|
||||
{
|
||||
for (auto &i : *hl)
|
||||
{
|
||||
const Option& o = opt[i];
|
||||
o.touch();
|
||||
add(o.get(1, 256), o.get_default(2, 16, default_port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void randomize(RandomAPI& rng)
|
||||
{
|
||||
std::shuffle(begin(), end(), rng());
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
for (auto &h : *this)
|
||||
os << h.to_string() << '\n';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
void add(const std::string& host,
|
||||
const std::string& port)
|
||||
{
|
||||
const std::string title = "host list";
|
||||
HostPort::validate_host(host, title);
|
||||
HostPort::validate_port(port, title);
|
||||
emplace_back(host, port);
|
||||
}
|
||||
};
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
|
||||
template <typename HOST>
|
||||
bool next(const List& list, HOST& host)
|
||||
{
|
||||
if (list.size() > 0)
|
||||
{
|
||||
if (++index >= list.size())
|
||||
index = 0;
|
||||
const Host& h = list[index];
|
||||
host.host = h.host;
|
||||
host.port = h.port;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int index;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Get hostname
|
||||
|
||||
#ifndef OPENVPN_COMMON_HOSTNAME_H
|
||||
#define OPENVPN_COMMON_HOSTNAME_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_ASIO
|
||||
#include <asio/ip/host_name.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
inline std::string get_hostname()
|
||||
{
|
||||
#ifdef USE_ASIO
|
||||
return asio::ip::host_name();
|
||||
#else
|
||||
return "HOSTNAME_UNDEFINED";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
// 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 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_COMMON_HOSTPORT_H
|
||||
#define OPENVPN_COMMON_HOSTPORT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace HostPort {
|
||||
OPENVPN_EXCEPTION(host_port_error);
|
||||
|
||||
inline bool is_valid_port(const unsigned int port)
|
||||
{
|
||||
return port < 65536;
|
||||
}
|
||||
|
||||
inline bool is_valid_port(const std::string& port, unsigned int *value = nullptr)
|
||||
{
|
||||
return parse_number_validate<unsigned int>(port, 5, 1, 65535, value);
|
||||
}
|
||||
|
||||
inline void validate_port(const std::string& port, const std::string& title, unsigned int *value = nullptr)
|
||||
{
|
||||
if (!is_valid_port(port, value))
|
||||
OPENVPN_THROW(host_port_error, "bad " << title << " port number: " << Unicode::utf8_printable(port, 16));
|
||||
}
|
||||
|
||||
inline void validate_port(const unsigned int port, const std::string& title)
|
||||
{
|
||||
if (!is_valid_port(port))
|
||||
OPENVPN_THROW(host_port_error, "bad " << title << " port number: " << port);
|
||||
}
|
||||
|
||||
inline unsigned short parse_port(const std::string& port, const std::string& title)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
validate_port(port, title, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// An IP address is also considered to be a valid host
|
||||
inline bool is_valid_host_char(const char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
|| (c >= '0' && c <= '9')
|
||||
|| c == '.'
|
||||
|| c == '-'
|
||||
|| c == ':'; // for IPv6
|
||||
}
|
||||
|
||||
inline bool is_valid_host(const std::string& host)
|
||||
{
|
||||
if (!host.length() || host.length() > 256)
|
||||
return false;
|
||||
for (const auto &c : host)
|
||||
{
|
||||
if (!is_valid_host_char(c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool is_valid_unix_sock_char(const unsigned char c)
|
||||
{
|
||||
return c >= 0x21 && c <= 0x7E;
|
||||
}
|
||||
|
||||
inline bool is_valid_unix_sock(const std::string& host)
|
||||
{
|
||||
if (!host.length() || host.length() > 256)
|
||||
return false;
|
||||
for (const auto &c : host)
|
||||
{
|
||||
if (!is_valid_unix_sock_char(c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void validate_host(const std::string& host, const std::string& title)
|
||||
{
|
||||
if (!is_valid_host(host))
|
||||
OPENVPN_THROW(host_port_error, "bad " << title << " host: " << Unicode::utf8_printable(host, 64));
|
||||
}
|
||||
|
||||
inline bool split_host_port(const std::string& str,
|
||||
std::string& host,
|
||||
std::string& port,
|
||||
const std::string& default_port,
|
||||
const bool allow_unix,
|
||||
unsigned int *port_save = nullptr)
|
||||
{
|
||||
if (port_save)
|
||||
*port_save = 0;
|
||||
const size_t pos = str.find_last_of(':');
|
||||
const size_t cb = str.find_last_of(']');
|
||||
if (pos != std::string::npos && (cb == std::string::npos || pos > cb))
|
||||
{
|
||||
// host:port or [host]:port specified
|
||||
host = str.substr(0, pos);
|
||||
port = str.substr(pos + 1);
|
||||
}
|
||||
else if (!default_port.empty())
|
||||
{
|
||||
// only host specified
|
||||
host = str;
|
||||
port = default_port;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
// unbracket host
|
||||
if (host.length() >= 2 && host[0] == '[' && host[host.length()-1] == ']')
|
||||
host = host.substr(1, host.length()-2);
|
||||
|
||||
if (allow_unix && port == "unix")
|
||||
return is_valid_unix_sock(host);
|
||||
else
|
||||
return is_valid_host(host) && is_valid_port(port, port_save);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(HAVE_JSONCPP)
|
||||
#define HAVE_JSON
|
||||
#include "json/json.h" // JsonCpp library
|
||||
#elif defined(HAVE_OPENVPN_COMMON)
|
||||
#define HAVE_JSON
|
||||
#define OPENVPN_JSON_INTERNAL
|
||||
#include <openvpn/common/json.hpp> // internal OpenVPN JSON implementation
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A set of lexical analyzer classes. These classes can be combined
|
||||
// with the methods in split.hpp to create parsers.
|
||||
|
||||
#ifndef OPENVPN_COMMON_LEX_H
|
||||
#define OPENVPN_COMMON_LEX_H
|
||||
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// This class (or others that define an is_space method) is used as a
|
||||
// template parameter to methods in split.hpp.
|
||||
struct SpaceMatch
|
||||
{
|
||||
static bool is_space(char c)
|
||||
{
|
||||
return string::is_space(c);
|
||||
}
|
||||
};
|
||||
|
||||
// A basic lexical analyzer that understands quoting
|
||||
class StandardLex
|
||||
{
|
||||
public:
|
||||
StandardLex() : in_quote_(false), backslash(false), ch(-1) {}
|
||||
|
||||
void put(char c)
|
||||
{
|
||||
if (backslash)
|
||||
{
|
||||
ch = c;
|
||||
backslash = false;
|
||||
}
|
||||
else if (c == '\\')
|
||||
{
|
||||
backslash = true;
|
||||
ch = -1;
|
||||
}
|
||||
else if (c == '\"')
|
||||
{
|
||||
in_quote_ = !in_quote_;
|
||||
ch = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = c;
|
||||
}
|
||||
}
|
||||
|
||||
bool available() const { return ch != -1; }
|
||||
int get() const { return ch; }
|
||||
void reset() { ch = -1; }
|
||||
|
||||
bool in_quote() const { return in_quote_; }
|
||||
|
||||
private:
|
||||
bool in_quote_;
|
||||
bool backslash;
|
||||
int ch;
|
||||
};
|
||||
|
||||
// A null lexical analyzer has no special understanding
|
||||
// of any particular string character.
|
||||
class NullLex
|
||||
{
|
||||
public:
|
||||
NullLex() : ch(-1) {}
|
||||
|
||||
void put(char c) { ch = c; }
|
||||
bool available() const { return ch != -1; }
|
||||
int get() const { return ch; }
|
||||
void reset() { ch = -1; }
|
||||
bool in_quote() const { return false; }
|
||||
|
||||
private:
|
||||
int ch;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_LEX_H
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 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_COMMON_LIKELY_H
|
||||
#define OPENVPN_COMMON_LIKELY_H
|
||||
|
||||
// Branch prediction hints (these make a difference on ARM)
|
||||
#if !defined(likely) && !defined(unlikely)
|
||||
#if defined(__GNUC__)
|
||||
# define likely(x) __builtin_expect((x),1)
|
||||
# define unlikely(x) __builtin_expect((x),0)
|
||||
#else
|
||||
# define likely(x) (x)
|
||||
# define unlikely(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // OPENVPN_COMMON_LIKELY_H
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 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_COMMON_LINK_H
|
||||
#define OPENVPN_COMMON_LINK_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Link creates a sender-receiver relationship between two objects.
|
||||
// SEND is the sender interface, while RECV is the receiver interface.
|
||||
// The receiver class should publicly inherit from Link and implement
|
||||
// the receiver interface.
|
||||
// Multiple inheritance of Link is intended for objects that play a
|
||||
// receiver role for multiple interfaces (such as the server-side
|
||||
// client instance object), and for this reason RECV common base
|
||||
// classes such as RC should be virtually inherited.
|
||||
// Usage of Link may create a memory reference cycle between
|
||||
// Link-derived objects and the SEND object. Therefore be sure
|
||||
// to manually break cycles as part of an explicit stop() method.
|
||||
template <typename SEND, typename RECV>
|
||||
class Link : public RECV
|
||||
{
|
||||
protected:
|
||||
Link() {}
|
||||
Link(typename SEND::Ptr send_arg) : send(std::move(send_arg)) {}
|
||||
Link(SEND* send_arg) : send(send_arg) {}
|
||||
|
||||
typename SEND::Ptr send;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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 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_COMMON_LOGROTATE_H
|
||||
#define OPENVPN_COMMON_LOGROTATE_H
|
||||
|
||||
#include <stdio.h> // for rename()
|
||||
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline void log_rotate(const std::string& fn, const int max_versions)
|
||||
{
|
||||
for (int i = max_versions - 1; i >= 0; --i)
|
||||
{
|
||||
std::string src;
|
||||
if (i)
|
||||
src = fn + '.' + to_string(i);
|
||||
else
|
||||
src = fn;
|
||||
std::string dest = fn + '.' + to_string(i+1);
|
||||
::rename(src.c_str(), dest.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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-2019 OpenVPN 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class LogSetup : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<LogSetup> Ptr;
|
||||
|
||||
virtual void reopen() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/arch.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
#include <openssl/crypto.h>
|
||||
#endif
|
||||
|
||||
// Does this architecture allow efficient unaligned access?
|
||||
|
||||
#if defined(OPENVPN_ARCH_x86_64) || defined(OPENVPN_ARCH_i386)
|
||||
#define OPENVPN_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
#endif
|
||||
|
||||
// Define a portable compiler memory access fence (from Boost).
|
||||
|
||||
#if defined(__INTEL_COMPILER)
|
||||
|
||||
#define OPENVPN_COMPILER_FENCE __memory_barrier();
|
||||
|
||||
#elif defined( _MSC_VER ) && _MSC_VER >= 1310
|
||||
|
||||
extern "C" void _ReadWriteBarrier();
|
||||
#pragma intrinsic( _ReadWriteBarrier )
|
||||
|
||||
#define OPENVPN_COMPILER_FENCE _ReadWriteBarrier();
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
#define OPENVPN_COMPILER_FENCE __asm__ __volatile__( "" : : : "memory" );
|
||||
|
||||
#else
|
||||
|
||||
#error need memory fence definition for this compiler
|
||||
|
||||
#endif
|
||||
|
||||
// C++ doesn't allow increment of void *
|
||||
|
||||
#define OPENVPN_INCR_VOID_PTR(var, incr) (var) = static_cast<const unsigned char*>(var) + (incr)
|
||||
|
||||
namespace openvpn {
|
||||
namespace crypto {
|
||||
// Clang on Android armeabi-v7a seems to have a compiler bug that compiles
|
||||
// the function in a segfaulting variant, use an alternative variant of
|
||||
// the function on all 32 bit arm to be safe
|
||||
|
||||
|
||||
/**
|
||||
* memneq - Compare two areas of memory in constant time
|
||||
*
|
||||
* @a: first area of memory
|
||||
* @b: second area of memory
|
||||
* @size: The length of the memory area to compare
|
||||
*
|
||||
* Returns false when data is equal, true otherwise
|
||||
*/
|
||||
inline bool memneq(const void *a, const void *b, size_t size);
|
||||
|
||||
#if defined(__arm__) && defined(USE_OPENSSL) && !defined(__aarch64__)
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
// memcmp does return 0 (=false) when the memory is equal. It normally
|
||||
// returns the position of first mismatch otherwise but the crypto
|
||||
// variants only promise to return something != 0 (=true)
|
||||
return (bool)(CRYPTO_memcmp(a, b, size));
|
||||
}
|
||||
#elif defined(__arm__) && !defined(__aarch64__)
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
// This is inspired by mbedtls' internal safer_memcmp function:
|
||||
const unsigned char *x = (const unsigned char *) a;
|
||||
const unsigned char *y = (const unsigned char *) b;
|
||||
unsigned char diff = 0;
|
||||
|
||||
for(size_t i = 0; i < size; i++ )
|
||||
{
|
||||
unsigned char u = x[i], v = y[i];
|
||||
diff |= u ^ v;
|
||||
}
|
||||
atomic_thread_fence(std::memory_order_release);
|
||||
return bool(diff);
|
||||
}
|
||||
#else
|
||||
#ifdef OPENVPN_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
enum { memneq_unaligned_ok = 1 };
|
||||
typedef size_t memneq_t;
|
||||
#else
|
||||
enum { memneq_unaligned_ok = 0 };
|
||||
typedef unsigned int memneq_t;
|
||||
#endif
|
||||
|
||||
// Is value of type T aligned on A boundary?
|
||||
// NOTE: requires that sizeof(A) is a power of 2
|
||||
template <typename T, typename A>
|
||||
inline bool is_aligned(const T value)
|
||||
{
|
||||
return (size_t(value) & (sizeof(A)-1)) == 0;
|
||||
}
|
||||
|
||||
// Returns true if we are allowed to dereference a and b that
|
||||
// point to objects of type memneq_t.
|
||||
inline bool memneq_deref_ok(const void *a, const void *b)
|
||||
{
|
||||
return memneq_unaligned_ok || (is_aligned<const void *, memneq_t>(a)|is_aligned<const void *, memneq_t>(b));
|
||||
}
|
||||
|
||||
// Constant-time memory equality method. Can be used in
|
||||
// security-sensitive contexts to inhibit timing attacks.
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
memneq_t neq = 0;
|
||||
|
||||
if (memneq_deref_ok(a, b))
|
||||
{
|
||||
while (size >= sizeof(memneq_t))
|
||||
{
|
||||
neq |= *(memneq_t *)a ^ *(memneq_t *)b;
|
||||
OPENVPN_INCR_VOID_PTR(a, sizeof(memneq_t));
|
||||
OPENVPN_INCR_VOID_PTR(b, sizeof(memneq_t));
|
||||
size -= sizeof(memneq_t);
|
||||
}
|
||||
}
|
||||
while (size > 0)
|
||||
{
|
||||
neq |= *(unsigned char *)a ^ *(unsigned char *)b;
|
||||
OPENVPN_INCR_VOID_PTR(a, 1);
|
||||
OPENVPN_INCR_VOID_PTR(b, 1);
|
||||
size -= 1;
|
||||
}
|
||||
OPENVPN_COMPILER_FENCE
|
||||
return bool(neq);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 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_COMMON_MODE_H
|
||||
#define OPENVPN_COMMON_MODE_H
|
||||
|
||||
// A client/server mode class.
|
||||
|
||||
namespace openvpn {
|
||||
class Mode
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
CLIENT,
|
||||
SERVER,
|
||||
};
|
||||
|
||||
Mode() : type_(CLIENT) {}
|
||||
explicit Mode(const Type t) : type_(t) {}
|
||||
|
||||
bool is_server() const { return type_ == SERVER; }
|
||||
bool is_client() const { return type_ == CLIENT; }
|
||||
|
||||
bool operator==(const Mode& other)
|
||||
{
|
||||
return type_ == other.type_;
|
||||
}
|
||||
|
||||
bool operator!=(const Mode& other)
|
||||
{
|
||||
return type_ != other.type_;
|
||||
}
|
||||
|
||||
const char *str() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case CLIENT:
|
||||
return "CLIENT";
|
||||
case SERVER:
|
||||
return "SERVER";
|
||||
default:
|
||||
return "UNDEF_MODE";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
};
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_MODE_H
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h> // Definition of AT_* constants */
|
||||
#include <sys/stat.h>
|
||||
#include <cstdint> // for std::uint64_t
|
||||
#include <cerrno>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_LINUX)
|
||||
|
||||
inline int update_file_mod_time_nanoseconds(const std::string& filename,
|
||||
const std::uint64_t nanoseconds_since_epooch)
|
||||
{
|
||||
struct timespec times[2];
|
||||
times[0].tv_sec = nanoseconds_since_epooch / std::uint64_t(1000000000);
|
||||
times[0].tv_nsec = nanoseconds_since_epooch % std::uint64_t(1000000000);
|
||||
times[1] = times[0];
|
||||
if (::utimensat(AT_FDCWD, filename.c_str(), times, 0) == -1)
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int update_file_mod_time_nanoseconds(const int fd,
|
||||
const std::uint64_t nanoseconds_since_epooch)
|
||||
{
|
||||
struct timespec times[2];
|
||||
times[0].tv_sec = nanoseconds_since_epooch / std::uint64_t(1000000000);
|
||||
times[0].tv_nsec = nanoseconds_since_epooch % std::uint64_t(1000000000);
|
||||
times[1] = times[0];
|
||||
if (::futimens(fd, times) == -1)
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline int update_file_mod_time_nanoseconds(const std::string& filename,
|
||||
const std::uint64_t nanoseconds_since_epooch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int update_file_mod_time_nanoseconds(const int fd,
|
||||
const std::uint64_t nanoseconds_since_epooch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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-2019 OpenVPN 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/>.
|
||||
|
||||
|
||||
// map/set find
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
namespace MSF {
|
||||
|
||||
template <typename ITERATOR>
|
||||
class Iter : public ITERATOR
|
||||
{
|
||||
public:
|
||||
template <typename MAP_SET>
|
||||
Iter(const MAP_SET& ms, ITERATOR&& iter)
|
||||
: ITERATOR(std::move(iter)),
|
||||
exists_(*this != ms.end())
|
||||
{
|
||||
}
|
||||
|
||||
Iter(ITERATOR&& iter)
|
||||
: ITERATOR(std::move(iter)),
|
||||
exists_(true)
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return exists_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool exists_;
|
||||
};
|
||||
|
||||
// Like ordinary map/set find, but returns an iterator
|
||||
// that defines an operator bool() method for testing if
|
||||
// the iterator is defined, so instead of:
|
||||
//
|
||||
// if (iter != map.end())
|
||||
// do_stuff();
|
||||
//
|
||||
// you can say:
|
||||
//
|
||||
// if (iter)
|
||||
// do_stuff();
|
||||
//
|
||||
template <typename MAP_SET, typename KEY>
|
||||
inline auto find(MAP_SET& ms, const KEY& k)
|
||||
{
|
||||
return Iter<decltype(ms.find(k))>(ms, ms.find(k));
|
||||
}
|
||||
|
||||
// Does key exist in map/set?
|
||||
template <typename MAP_SET, typename KEY>
|
||||
inline bool exists(MAP_SET& ms, const KEY& k)
|
||||
{
|
||||
return ms.find(k) != ms.end();
|
||||
}
|
||||
|
||||
// Convert an ordinary, dereferenceable iterator to an MSF::Iter
|
||||
template <typename ITERATOR>
|
||||
inline auto iter(ITERATOR i)
|
||||
{
|
||||
return Iter<ITERATOR>(std::move(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// 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 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_COMMON_MSGWIN_H
|
||||
#define OPENVPN_COMMON_MSGWIN_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
// Fundamental, lowest-level object of OpenVPN protocol reliability layer
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// MessageWindow --
|
||||
// On receiving side: used to order packets which may be received out-of-order
|
||||
// On sending side: used to buffer unacknowledged packets
|
||||
// M : message class, must define default constructor, defined(), and erase() methods
|
||||
// id_t : sequence number object, usually unsigned int
|
||||
template <typename M, typename id_t>
|
||||
class MessageWindow
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(message_window_ref_by_id);
|
||||
OPENVPN_SIMPLE_EXCEPTION(message_window_rm_head);
|
||||
|
||||
MessageWindow()
|
||||
: head_id_(0), span_(0) {}
|
||||
|
||||
MessageWindow(const id_t starting_head_id, const id_t span)
|
||||
: head_id_(starting_head_id), span_(span) {}
|
||||
|
||||
void init(const id_t starting_head_id, const id_t span)
|
||||
{
|
||||
head_id_ = starting_head_id;
|
||||
span_ = span;
|
||||
q_.clear();
|
||||
}
|
||||
|
||||
// Return true if id is within current window
|
||||
bool in_window(const id_t id) const
|
||||
{
|
||||
return id >= head_id_ && id < head_id_ + span_;
|
||||
}
|
||||
|
||||
// Return true if id is before current window
|
||||
bool pre_window(const id_t id) const
|
||||
{
|
||||
return id < head_id_;
|
||||
}
|
||||
|
||||
// Return a reference to M object at id, throw exception
|
||||
// if id is not in current window
|
||||
M& ref_by_id(const id_t id)
|
||||
{
|
||||
if (in_window(id))
|
||||
{
|
||||
grow(id);
|
||||
return q_[id - head_id_];
|
||||
}
|
||||
else
|
||||
throw message_window_ref_by_id();
|
||||
}
|
||||
|
||||
// Remove the M object at id, is a no-op if
|
||||
// id not in window. Do a purge() as a last
|
||||
// step to advance the head_id_ if it's now
|
||||
// pointing at undefined M objects.
|
||||
void rm_by_id(const id_t id)
|
||||
{
|
||||
if (in_window(id))
|
||||
{
|
||||
grow(id);
|
||||
M& m = q_[id - head_id_];
|
||||
m.erase();
|
||||
}
|
||||
purge();
|
||||
}
|
||||
|
||||
// Return true if an object at head of queue is defined
|
||||
bool head_defined() const
|
||||
{
|
||||
return (!q_.empty() && q_.front().defined());
|
||||
}
|
||||
|
||||
// Return the id that the object at the head of the queue
|
||||
// would have (even if it isn't defined yet).
|
||||
id_t head_id() const { return head_id_; }
|
||||
|
||||
// Return the id of one past the end of the window
|
||||
id_t tail_id() const { return head_id_ + span_; }
|
||||
|
||||
// Return the window size
|
||||
id_t span() const { return span_; }
|
||||
|
||||
// Return a reference to the object at the front of the queue
|
||||
M& ref_head() { return q_.front(); }
|
||||
|
||||
// Remove the object at head of queue, throw an exception if undefined
|
||||
void rm_head()
|
||||
{
|
||||
if (head_defined())
|
||||
rm_head_nocheck();
|
||||
else
|
||||
throw message_window_rm_head();
|
||||
}
|
||||
|
||||
// Remove the object at head of queue without error checking (other than
|
||||
// that provided by std::deque object). Don't call this method unless
|
||||
// head_defined() returns true.
|
||||
void rm_head_nocheck()
|
||||
{
|
||||
q_.front().erase();
|
||||
q_.pop_front();
|
||||
++head_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Expand the queue if necessary so that id maps
|
||||
// to an object in the queue
|
||||
void grow(const id_t id)
|
||||
{
|
||||
const size_t needed_index = id - head_id_;
|
||||
while (q_.size() <= needed_index)
|
||||
q_.push_back(M());
|
||||
}
|
||||
|
||||
// Purge all undefined objects at the head of
|
||||
// the queue, advancing the head_id_
|
||||
void purge()
|
||||
{
|
||||
while (!q_.empty() && q_.front().erased())
|
||||
{
|
||||
q_.pop_front();
|
||||
++head_id_;
|
||||
}
|
||||
}
|
||||
|
||||
id_t head_id_; // id of msgs[0]
|
||||
id_t span_;
|
||||
std::deque<M> q_;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_MSGWIN_H
|
||||
@@ -0,0 +1,152 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General purpose methods for dealing with numbers.
|
||||
|
||||
#ifndef OPENVPN_COMMON_NUMBER_H
|
||||
#define OPENVPN_COMMON_NUMBER_H
|
||||
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_EXCEPTION(number_parse_exception);
|
||||
|
||||
// Parse the number of type T in str, returning
|
||||
// value in retval. Returns true on success.
|
||||
// Note -- currently doesn't detect overflow.
|
||||
// If nondigit_term is true, any non-digit char
|
||||
// can terminate the numerical value. Otherwise,
|
||||
// only '\0' can terminate the value.
|
||||
template <typename T>
|
||||
inline bool parse_number(const char *str,
|
||||
T& retval,
|
||||
const bool nondigit_term = false)
|
||||
{
|
||||
if (!str[0])
|
||||
return false; // empty string
|
||||
bool neg = false;
|
||||
size_t i = 0;
|
||||
if (std::numeric_limits<T>::min() < 0 && str[0] == '-')
|
||||
{
|
||||
neg = true;
|
||||
i = 1;
|
||||
}
|
||||
T ret = T(0);
|
||||
while (true)
|
||||
{
|
||||
const char c = str[i++];
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
ret *= T(10);
|
||||
ret += T(c - '0');
|
||||
}
|
||||
else if (!c || nondigit_term)
|
||||
{
|
||||
retval = neg ? -ret : ret;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false; // non-digit
|
||||
}
|
||||
}
|
||||
|
||||
// like parse_number above, but accepts std::string
|
||||
template <typename T>
|
||||
inline bool parse_number(const std::string& str, T& retval)
|
||||
{
|
||||
return parse_number<T>(str.c_str(), retval);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T parse_number_throw(const std::string& str, const std::string& error)
|
||||
{
|
||||
T ret;
|
||||
if (parse_number<T>(str.c_str(), ret))
|
||||
return ret;
|
||||
else
|
||||
throw number_parse_exception(error);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T parse_number_throw(const std::string& str, const char *error)
|
||||
{
|
||||
T ret;
|
||||
if (parse_number<T>(str.c_str(), ret))
|
||||
return ret;
|
||||
else
|
||||
throw number_parse_exception(std::string(error));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T parse_number_throw(const char *str, const char *error)
|
||||
{
|
||||
T ret;
|
||||
if (parse_number<T>(str, ret))
|
||||
return ret;
|
||||
else
|
||||
throw number_parse_exception(std::string(error));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool parse_number_validate(const std::string& numstr,
|
||||
const size_t max_len,
|
||||
const T minimum,
|
||||
const T maximum,
|
||||
T* value_return = nullptr)
|
||||
{
|
||||
if (numstr.length() <= max_len)
|
||||
{
|
||||
T value;
|
||||
if (parse_number<T>(numstr.c_str(), value))
|
||||
{
|
||||
if (value >= minimum && value <= maximum)
|
||||
{
|
||||
if (value_return)
|
||||
*value_return = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_number(const char *str)
|
||||
{
|
||||
char c;
|
||||
bool found_digit = false;
|
||||
while ((c = *str++))
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
found_digit = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return found_digit;
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_NUMBER_H
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 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_COMMON_OLONG_H
|
||||
#define OPENVPN_COMMON_OLONG_H
|
||||
|
||||
// opportunistic long -- 32 bits on 32-bit machines, and 64 bits
|
||||
// on 64-bit machines.
|
||||
|
||||
namespace openvpn {
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
typedef long long olong;
|
||||
typedef unsigned long long oulong;
|
||||
#else
|
||||
typedef long olong;
|
||||
typedef unsigned long oulong;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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-2018 OpenVPN 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
OPENVPN_EXCEPTION(option_error);
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A helper macro for setting up an arbitrary class
|
||||
// to support stringstream concatenation using <<
|
||||
|
||||
#ifndef OPENVPN_COMMON_OSTREAM_H
|
||||
#define OPENVPN_COMMON_OSTREAM_H
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#define OPENVPN_OSTREAM(TYPE, METH) \
|
||||
template <typename Elem, typename Traits> \
|
||||
std::basic_ostream<Elem, Traits>& operator<<( \
|
||||
std::basic_ostream<Elem, Traits>& os, const TYPE& addr) \
|
||||
{ \
|
||||
os << addr.METH(); \
|
||||
return os; \
|
||||
}
|
||||
|
||||
#endif // OPENVPN_COMMON_OSTREAM_H
|
||||
@@ -0,0 +1,207 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General-purpose methods for handling filesystem pathnames
|
||||
|
||||
#ifndef OPENVPN_COMMON_PATH_H
|
||||
#define OPENVPN_COMMON_PATH_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace path {
|
||||
|
||||
// Directory separators. The first char in dirsep is the primary
|
||||
// separator for the platform, while subsequent chars are also
|
||||
// recognized as separators.
|
||||
namespace {
|
||||
#if defined(OPENVPN_PLATFORM_WIN) || defined(OPENVPN_PATH_SIMULATE_WINDOWS)
|
||||
// Windows
|
||||
const char dirsep[] = "\\/"; // CONST GLOBAL
|
||||
#else
|
||||
// Unix
|
||||
const char dirsep[] = "/\\"; // CONST GLOBAL
|
||||
#endif
|
||||
}
|
||||
|
||||
// true if char is a directory separator
|
||||
inline bool is_dirsep(const char c)
|
||||
{
|
||||
for (const char *p = dirsep; *p != '\0'; ++p)
|
||||
if (c == *p)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool win_dev(const std::string& path, const bool fully_qualified)
|
||||
{
|
||||
#if defined(OPENVPN_PLATFORM_WIN) || defined(OPENVPN_PATH_SIMULATE_WINDOWS)
|
||||
// Identify usage such as "c:\\".
|
||||
return path.length() >= 3
|
||||
&& ((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
|
||||
&& path[1] == ':'
|
||||
&& (!fully_qualified || is_dirsep(path[2]));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// true if path is fully qualified
|
||||
inline bool is_fully_qualified(const std::string& path)
|
||||
{
|
||||
return win_dev(path, true) || (path.length() > 0 && is_dirsep(path[0]));
|
||||
}
|
||||
|
||||
// does path refer to regular file without directory traversal
|
||||
inline bool is_flat(const std::string& path)
|
||||
{
|
||||
return path.length() > 0
|
||||
&& path != "."
|
||||
&& path != ".."
|
||||
&& path.find_first_of(dirsep) == std::string::npos
|
||||
&& !win_dev(path, false);
|
||||
}
|
||||
|
||||
inline std::string basename(const std::string& path)
|
||||
{
|
||||
const size_t pos = path.find_last_of(dirsep);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
const size_t p = pos + 1;
|
||||
if (p >= path.length())
|
||||
return "";
|
||||
else
|
||||
return path.substr(p);
|
||||
}
|
||||
else
|
||||
return path;
|
||||
}
|
||||
|
||||
inline std::string dirname(const std::string& path)
|
||||
{
|
||||
const size_t pos = path.find_last_of(dirsep);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
if (pos == 0)
|
||||
return "/";
|
||||
else
|
||||
return path.substr(0, pos);
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
// return true if path is a regular file that doesn't try to traverse via ".." or "/..."
|
||||
inline bool is_contained(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
return false;
|
||||
if (win_dev(path, false))
|
||||
return false;
|
||||
if (is_dirsep(path[0]))
|
||||
return false;
|
||||
|
||||
// look for ".." in path
|
||||
enum State {
|
||||
SEP, // immediately after separator
|
||||
MID, // middle of dir
|
||||
DOT_2, // looking for second '.'
|
||||
POST_DOT_2, // after ".."
|
||||
};
|
||||
State state = SEP;
|
||||
for (const auto c : path)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SEP:
|
||||
if (c == '.')
|
||||
state = DOT_2;
|
||||
else if (!is_dirsep(c))
|
||||
state = MID;
|
||||
break;
|
||||
case MID:
|
||||
if (is_dirsep(c))
|
||||
state = SEP;
|
||||
break;
|
||||
case DOT_2:
|
||||
if (c == '.')
|
||||
state = POST_DOT_2;
|
||||
else if (is_dirsep(c))
|
||||
state = SEP;
|
||||
else
|
||||
state = MID;
|
||||
break;
|
||||
case POST_DOT_2:
|
||||
if (is_dirsep(c))
|
||||
return false;
|
||||
state = MID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return state != POST_DOT_2;
|
||||
}
|
||||
|
||||
inline std::string ext(const std::string& basename)
|
||||
{
|
||||
const size_t pos = basename.find_last_of('.');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
const size_t p = pos + 1;
|
||||
if (p >= basename.length())
|
||||
return "";
|
||||
else
|
||||
return basename.substr(p);
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
inline std::string root(const std::string& basename)
|
||||
{
|
||||
const size_t pos = basename.find_last_of('.');
|
||||
if (pos != std::string::npos)
|
||||
return basename.substr(0, pos);
|
||||
else
|
||||
return basename;
|
||||
}
|
||||
|
||||
inline std::string join(const std::string& p1, const std::string& p2)
|
||||
{
|
||||
if (p1.empty() || is_fully_qualified(p2))
|
||||
return p2;
|
||||
else
|
||||
return string::add_trailing_copy(p1, dirsep[0]) + p2;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::string join(const std::string& p1, const std::string& p2, Args... args)
|
||||
{
|
||||
return join(join(p1, p2), args...);
|
||||
}
|
||||
|
||||
} // namespace path
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_STRING_H
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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 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_COMMON_PEERCRED_H
|
||||
#define OPENVPN_COMMON_PEERCRED_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_TYPE_APPLE
|
||||
#include <sys/ucred.h>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace SockOpt {
|
||||
|
||||
struct Creds
|
||||
{
|
||||
Creds(const int uid_arg=-1, const int gid_arg=-1, const int pid_arg=-1)
|
||||
: uid(uid_arg),
|
||||
gid(gid_arg),
|
||||
pid(pid_arg)
|
||||
{
|
||||
}
|
||||
|
||||
bool root_or_self_uid() const
|
||||
{
|
||||
return !uid || uid == ::getuid();
|
||||
}
|
||||
|
||||
bool root_uid() const
|
||||
{
|
||||
return !uid;
|
||||
}
|
||||
|
||||
bool match_uid(const int other_uid) const
|
||||
{
|
||||
return uid >= 0 && uid == other_uid;
|
||||
}
|
||||
|
||||
int uid;
|
||||
int gid;
|
||||
int pid;
|
||||
};
|
||||
|
||||
// get credentials of process on other side of unix socket
|
||||
inline bool peercreds(const int fd, Creds& cr)
|
||||
{
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_APPLE)
|
||||
xucred cred;
|
||||
socklen_t credLen = sizeof(cred);
|
||||
if (::getsockopt(fd, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) != 0)
|
||||
return false;
|
||||
cr = Creds(cred.cr_uid, cred.cr_gid);
|
||||
return true;
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX)
|
||||
struct ucred uc;
|
||||
socklen_t uc_len = sizeof(uc);
|
||||
if (::getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &uc_len) != 0)
|
||||
return false;
|
||||
cr = Creds(uc.uid, uc.gid, uc.pid);
|
||||
return true;
|
||||
#else
|
||||
#error no implementation for peercreds()
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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 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_COMMON_PERSISTFILE_H
|
||||
#define OPENVPN_COMMON_PERSISTFILE_H
|
||||
|
||||
#include <sys/types.h> // for open(), lseek(), ftruncate()
|
||||
#include <sys/stat.h> // for open()
|
||||
#include <fcntl.h> // for open()
|
||||
#include <unistd.h> // for write(), lseek(), ftruncate()
|
||||
#include <errno.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/write.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class PersistentFile
|
||||
{
|
||||
public:
|
||||
PersistentFile(const std::string& fn_arg)
|
||||
: fn(fn_arg)
|
||||
{
|
||||
const int f = ::open(fn.c_str(), O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
|
||||
if (f < 0)
|
||||
syserr("open");
|
||||
fd.reset(f);
|
||||
}
|
||||
|
||||
void write(const void *buf, size_t count)
|
||||
{
|
||||
const off_t off = ::lseek(fd(), 0, SEEK_SET);
|
||||
if (off < 0)
|
||||
syserr("seek");
|
||||
if (off)
|
||||
err("unexpected seek");
|
||||
if (::ftruncate(fd(), 0) < 0)
|
||||
syserr("truncate");
|
||||
const ssize_t len = write_retry(fd(), buf, count);
|
||||
if (len < 0)
|
||||
syserr("write");
|
||||
if (len != count || len != ::lseek(fd(), 0, SEEK_CUR))
|
||||
err("incomplete write");
|
||||
if (::ftruncate(fd(), len) < 0)
|
||||
syserr("truncate");
|
||||
}
|
||||
|
||||
struct stat stat()
|
||||
{
|
||||
struct stat s;
|
||||
if (::fstat(fd(), &s) < 0)
|
||||
syserr("fstat");
|
||||
return s;
|
||||
}
|
||||
|
||||
void write(const Buffer& buf)
|
||||
{
|
||||
write(buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
void write(const std::string& str)
|
||||
{
|
||||
write(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
private:
|
||||
void syserr(const char *type)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW_EXCEPTION(fn << " : " << type << " error : " << strerror_str(eno));
|
||||
}
|
||||
|
||||
void err(const char *type)
|
||||
{
|
||||
OPENVPN_THROW_EXCEPTION(fn << " : " << type << " error");
|
||||
}
|
||||
|
||||
std::string fn;
|
||||
ScopedFD fd;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
// 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 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_COMMON_PIPE_H
|
||||
#define OPENVPN_COMMON_PIPE_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Pipe {
|
||||
class SD
|
||||
{
|
||||
public:
|
||||
SD(openvpn_io::io_context& io_context, ScopedFD& fd)
|
||||
{
|
||||
if (fd.defined())
|
||||
sd.reset(new openvpn_io::posix::stream_descriptor(io_context, fd.release()));
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return bool(sd);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<openvpn_io::posix::stream_descriptor> sd;
|
||||
};
|
||||
|
||||
class SD_OUT : public SD
|
||||
{
|
||||
public:
|
||||
SD_OUT(openvpn_io::io_context& io_context, const std::string& content, ScopedFD& fd)
|
||||
: SD(io_context, fd)
|
||||
{
|
||||
if (defined())
|
||||
{
|
||||
buf = buf_alloc_from_string(content);
|
||||
queue_write();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void queue_write()
|
||||
{
|
||||
sd->async_write_some(buf.const_buffer_limit(2048),
|
||||
[this](const openvpn_io::error_code& ec, const size_t bytes_sent) {
|
||||
if (!ec && bytes_sent < buf.size())
|
||||
{
|
||||
buf.advance(bytes_sent);
|
||||
queue_write();
|
||||
}
|
||||
else
|
||||
{
|
||||
sd->close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
class SD_IN : public SD
|
||||
{
|
||||
public:
|
||||
SD_IN(openvpn_io::io_context& io_context, ScopedFD& fd)
|
||||
: SD(io_context, fd)
|
||||
{
|
||||
if (defined())
|
||||
queue_read();
|
||||
}
|
||||
|
||||
const std::string content() const
|
||||
{
|
||||
return data.to_string();
|
||||
}
|
||||
|
||||
private:
|
||||
void queue_read()
|
||||
{
|
||||
buf.reset(0, 2048, 0);
|
||||
sd->async_read_some(buf.mutable_buffer_clamp(),
|
||||
[this](const openvpn_io::error_code& ec, const size_t bytes_recvd) {
|
||||
if (!ec)
|
||||
{
|
||||
buf.set_size(bytes_recvd);
|
||||
data.put_consume(buf);
|
||||
queue_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
sd->close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BufferAllocated buf;
|
||||
BufferList data;
|
||||
};
|
||||
|
||||
inline void make_pipe(int fd[2])
|
||||
{
|
||||
if (::pipe(fd) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW_EXCEPTION("error creating pipe : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
|
||||
inline void make_pipe(ScopedFD& read, ScopedFD& write)
|
||||
{
|
||||
int fd[2];
|
||||
make_pipe(fd);
|
||||
read.reset(fd[0]);
|
||||
write.reset(fd[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 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/>.
|
||||
|
||||
// define a TARGET_x macro that describes our build target
|
||||
|
||||
#ifndef OPENVPN_COMMON_PLATFORM_H
|
||||
#define OPENVPN_COMMON_PLATFORM_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define OPENVPN_PLATFORM_WIN
|
||||
# if defined(__cplusplus_winrt)
|
||||
# include <winapifamily.h>
|
||||
# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
# define OPENVPN_PLATFORM_UWP
|
||||
# endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||
# endif // defined(__cplusplus_winrt)
|
||||
#elif defined(__APPLE__)
|
||||
# include "TargetConditionals.h"
|
||||
# define OPENVPN_PLATFORM_TYPE_APPLE
|
||||
# if TARGET_OS_IPHONE // includes iPad
|
||||
# define OPENVPN_PLATFORM_IPHONE
|
||||
# define OPENVPN_PLATFORM_IPHONE_DEVICE
|
||||
# elif TARGET_IPHONE_SIMULATOR // includes iPad
|
||||
# define OPENVPN_PLATFORM_IPHONE
|
||||
# define OPENVPN_PLATFORM_IPHONE_SIMULATOR
|
||||
# elif TARGET_OS_MAC
|
||||
# define OPENVPN_PLATFORM_MAC
|
||||
# endif
|
||||
#elif defined(__ANDROID__)
|
||||
# define OPENVPN_PLATFORM_ANDROID
|
||||
#elif defined(__linux__)
|
||||
# define OPENVPN_PLATFORM_LINUX
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#define OPENVPN_PLATFORM_TYPE_UNIX
|
||||
#endif
|
||||
|
||||
#endif // OPENVPN_COMMON_PLATFORM_H
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 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_COMMON_PLATFORM_NAME_H
|
||||
#define OPENVPN_COMMON_PLATFORM_NAME_H
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// return a string that describes our platform
|
||||
inline const char *platform_name()
|
||||
{
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
#if defined(OPENVPN_PLATFORM_UWP)
|
||||
return "uwp";
|
||||
#else
|
||||
return "win";
|
||||
#endif // UWP
|
||||
#elif defined(OPENVPN_PLATFORM_MAC)
|
||||
return "mac";
|
||||
#elif defined(OPENVPN_PLATFORM_IPHONE)
|
||||
return "ios";
|
||||
#elif defined(OPENVPN_PLATFORM_IPHONE_SIMULATOR)
|
||||
return "iosim";
|
||||
#elif defined(OPENVPN_PLATFORM_ANDROID)
|
||||
return "android";
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX)
|
||||
return "linux";
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_PLATFORM_NAME_H
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 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_COMMON_PLATFORM_STRING_H
|
||||
#define OPENVPN_COMMON_PLATFORM_STRING_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/version.hpp>
|
||||
#include <openvpn/common/platform_name.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline std::string platform_string(const std::string& title, const std::string& app_version)
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
os << title << " ";
|
||||
if (!app_version.empty())
|
||||
os << app_version << '/';
|
||||
os << OPENVPN_VERSION;
|
||||
os << ' ' << platform_name();
|
||||
# if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
|
||||
os << " x86_64";
|
||||
# elif defined(__i386__) || defined(_M_IX86)
|
||||
os << " i386";
|
||||
# elif defined(__aarch64__) || defined(__arm64__)
|
||||
os << " arm64";
|
||||
# elif defined(__arm__) || defined(_M_ARM)
|
||||
# if defined(__ARM_ARCH_7S__) || defined(_ARM_ARCH_7S)
|
||||
os << " armv7s";
|
||||
# elif defined(__ARM_ARCH_7A__)
|
||||
os << " armv7a";
|
||||
# elif defined(__ARM_V7__) || defined(_ARM_ARCH_7)
|
||||
os << " armv7";
|
||||
# else
|
||||
os << " arm";
|
||||
# endif
|
||||
# if defined(__thumb2__)
|
||||
os << " thumb2";
|
||||
# elif defined(__thumb__) || defined(_M_ARMT)
|
||||
os << " thumb";
|
||||
# endif
|
||||
# endif
|
||||
|
||||
os << ' ' << (sizeof(void *) * 8) << "-bit";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
inline std::string platform_string()
|
||||
{
|
||||
return platform_string("OpenVPN core", "");
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_PLATFORM_STRING_H
|
||||
@@ -0,0 +1,184 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General-purpose classes for instantiating a posix process with arguments.
|
||||
|
||||
#ifndef OPENVPN_COMMON_PROCESS_H
|
||||
#define OPENVPN_COMMON_PROCESS_H
|
||||
|
||||
#include <stdlib.h> // exit
|
||||
#include <unistd.h> // fork, execve
|
||||
#include <sys/types.h> // waitpid
|
||||
#include <sys/wait.h> // waitpid
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/common/redir.hpp>
|
||||
#include <openvpn/common/signal.hpp>
|
||||
#include <openvpn/common/argv.hpp>
|
||||
#include <openvpn/common/environ.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// low-level fork/exec (async)
|
||||
inline pid_t system_cmd_async(const std::string& cmd,
|
||||
const Argv& argv,
|
||||
const Environ* env,
|
||||
RedirectBase* redir)
|
||||
{
|
||||
ArgvWrapper argv_wrap(argv);
|
||||
std::unique_ptr<ArgvWrapper> env_wrap;
|
||||
if (env)
|
||||
env_wrap.reset(new ArgvWrapper(*env));
|
||||
auto fn = cmd.c_str();
|
||||
auto av = argv_wrap.c_argv();
|
||||
auto ev = env_wrap ? env_wrap->c_argv() : ::environ;
|
||||
const pid_t pid = redir ? ::fork() : ::vfork();
|
||||
if (pid == pid_t(0)) /* child side */
|
||||
{
|
||||
if (redir)
|
||||
redir->redirect();
|
||||
::execve(fn, av, ev);
|
||||
::_exit(127);
|
||||
}
|
||||
else if (pid < pid_t(0)) /* fork failed */
|
||||
return -1;
|
||||
else /* parent side */
|
||||
{
|
||||
if (redir)
|
||||
redir->close();
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
// completion for system_cmd_async()
|
||||
inline int system_cmd_post(const pid_t pid)
|
||||
{
|
||||
int status = -1;
|
||||
if (::waitpid(pid, &status, 0) == pid)
|
||||
{
|
||||
if (WIFEXITED(status))
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// synchronous version of system_cmd_async
|
||||
inline int system_cmd(const std::string& cmd,
|
||||
const Argv& argv,
|
||||
RedirectBase* redir,
|
||||
const Environ* env)
|
||||
{
|
||||
const pid_t pid = system_cmd_async(cmd, argv, env, redir);
|
||||
if (pid < pid_t(0))
|
||||
return -1;
|
||||
return system_cmd_post(pid);
|
||||
}
|
||||
|
||||
// simple command execution
|
||||
inline int system_cmd(const std::string& cmd, const Argv& argv)
|
||||
{
|
||||
return system_cmd(cmd, argv, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// simple command execution
|
||||
inline int system_cmd(const Argv& argv)
|
||||
{
|
||||
int ret = -1;
|
||||
if (argv.size())
|
||||
ret = system_cmd(argv[0], argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// command execution with std::strings as
|
||||
// input/output/error (uses pipes under the
|
||||
// hood)
|
||||
inline int system_cmd(const std::string& cmd,
|
||||
const Argv& argv,
|
||||
const Environ* env,
|
||||
RedirectPipe::InOut& inout,
|
||||
unsigned int redirect_pipe_flags)
|
||||
{
|
||||
SignalBlockerPipe sbpipe;
|
||||
RedirectPipe remote;
|
||||
if (!inout.in.empty())
|
||||
redirect_pipe_flags |= RedirectPipe::ENABLE_IN;
|
||||
RedirectPipe local(remote, redirect_pipe_flags);
|
||||
const pid_t pid = system_cmd_async(cmd, argv, env, &remote);
|
||||
if (pid < pid_t(0))
|
||||
return -1;
|
||||
local.transact(inout);
|
||||
return system_cmd_post(pid);
|
||||
}
|
||||
|
||||
struct Command : public Action
|
||||
{
|
||||
typedef RCPtr<Command> Ptr;
|
||||
|
||||
Command() {}
|
||||
|
||||
Command(Argv argv_arg)
|
||||
: argv(std::move(argv_arg))
|
||||
{
|
||||
}
|
||||
|
||||
Command* copy() const
|
||||
{
|
||||
Command* ret = new Command;
|
||||
ret->argv = argv;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void execute(std::ostream& os) override
|
||||
{
|
||||
if (!argv.empty())
|
||||
{
|
||||
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, RedirectPipe::COMBINE_OUT_ERR);
|
||||
if (status < 0)
|
||||
os << "Error: command failed to execute" << std::endl;
|
||||
os << inout.out;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
os << "Error: command called with empty argv" << std::endl;
|
||||
}
|
||||
|
||||
virtual std::string to_string() const override
|
||||
{
|
||||
return argv.to_string();
|
||||
}
|
||||
|
||||
Argv argv;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OPENVPN_COMMON_PROCESS_H
|
||||
@@ -0,0 +1,143 @@
|
||||
// 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 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_COMMON_PTHREADCOND_H
|
||||
#define OPENVPN_COMMON_PTHREADCOND_H
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
#include <openvpn/common/stop.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Barrier class that is useful in cases where all threads
|
||||
// need to reach a known point before executing some action.
|
||||
// Note that this barrier implementation is
|
||||
// constructed using C++11 condition variables.
|
||||
class PThreadBarrier
|
||||
{
|
||||
enum State {
|
||||
UNSIGNALED=0, // initial state
|
||||
SIGNALED, // signal() was called
|
||||
ERROR_THROWN, // error() was called
|
||||
};
|
||||
|
||||
public:
|
||||
// status return from wait()
|
||||
enum Status {
|
||||
SUCCESS=0, // successful
|
||||
CHOSEN_ONE, // successful and chosen (only one thread is chosen)
|
||||
TIMEOUT, // timeout
|
||||
ERROR, // at least one thread called error()
|
||||
};
|
||||
|
||||
PThreadBarrier(const int initial_limit = -1)
|
||||
: stop(nullptr),
|
||||
limit(initial_limit)
|
||||
{
|
||||
}
|
||||
|
||||
PThreadBarrier(Stop* stop_arg, const int initial_limit = -1)
|
||||
: stop(stop_arg),
|
||||
limit(initial_limit)
|
||||
{
|
||||
}
|
||||
|
||||
// All callers will increment count and block until
|
||||
// count == limit. CHOSEN_ONE will be returned to
|
||||
// the first caller to reach limit. This caller can
|
||||
// then release all the other callers by calling
|
||||
// signal().
|
||||
int wait(const unsigned int seconds)
|
||||
{
|
||||
// allow asynchronous stop
|
||||
Stop::Scope stop_scope(stop, [this]() {
|
||||
error();
|
||||
});
|
||||
|
||||
bool timeout = false;
|
||||
int ret;
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
const unsigned int c = ++count;
|
||||
while (state == UNSIGNALED
|
||||
&& (limit < 0 || c < limit)
|
||||
&& !timeout)
|
||||
timeout = (cv.wait_for(lock, std::chrono::seconds(seconds)) == std::cv_status::timeout);
|
||||
if (timeout)
|
||||
ret = TIMEOUT;
|
||||
else if (state == ERROR_THROWN)
|
||||
ret = ERROR;
|
||||
else if (state == UNSIGNALED && !chosen)
|
||||
{
|
||||
ret = CHOSEN_ONE;
|
||||
chosen = true;
|
||||
}
|
||||
else
|
||||
ret = SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_limit(const int new_limit)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
limit = new_limit;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
// Generally, only the CHOSEN_ONE calls signal() after its work
|
||||
// is complete, to allow the other threads to pass the barrier.
|
||||
void signal()
|
||||
{
|
||||
signal_(SIGNALED);
|
||||
}
|
||||
|
||||
// Causes all threads waiting on wait() (and those which call wait()
|
||||
// in the future) to exit with ERROR status.
|
||||
void error()
|
||||
{
|
||||
signal_(ERROR_THROWN);
|
||||
}
|
||||
|
||||
private:
|
||||
void signal_(const State newstate)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (state == UNSIGNALED)
|
||||
{
|
||||
state = newstate;
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
Stop* stop;
|
||||
State state{UNSIGNALED};
|
||||
bool chosen = false;
|
||||
unsigned int count = 0;
|
||||
int limit;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,733 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A basic reference-counting garbage collection scheme based
|
||||
// on intrusive pointers, where the reference count is embedded in
|
||||
// the object via inheritance. Simply inherit from RC to create an
|
||||
// object that can be tracked with an RCPtr.
|
||||
//
|
||||
// We use tend to use RCPtr (or RCWeakPtr) rather than the other
|
||||
// smart pointer classes (std or boost) for flexibility and
|
||||
// performance.
|
||||
//
|
||||
// Smart pointers have two basic attributes that determine
|
||||
// their performance. Either of these attributes, when required,
|
||||
// will degrade the performance of the smart pointer:
|
||||
//
|
||||
// 1. whether the smart pointer is thread-safe, i.e. it uses an
|
||||
// atomic reference counter
|
||||
// 2. whether the smart pointer can be referrred to via a
|
||||
// weak reference
|
||||
//
|
||||
// In keeping with the oft-stated C++ motto of only paying for
|
||||
// what you use, both attributes can be independently controlled.
|
||||
//
|
||||
// * thread-unsafe/not-weak-referenceable -- class Foo : public RC<thread_unsafe_refcount>
|
||||
// * thread-safe/not-weak-referenceable -- class Foo : public RC<thread_safe_refcount>
|
||||
// * thread-unsafe/weak-referenceable -- class Foo : public RCWeak<thread_unsafe_refcount>
|
||||
// * thread-safe/weak-referenceable -- class Foo : public RCWeak<thread_safe_refcount>
|
||||
//
|
||||
// Thread-safe reference counting can be significantly more expensive
|
||||
// because an atomic object must be used for the reference count.
|
||||
// Therefore, thread-safe reference counting should only be used for
|
||||
// objects that have visibility across multiple threads.
|
||||
//
|
||||
// In addition, having an object be weak-referenceable also
|
||||
// imposes a cost, so it should be avoided unless necessary.
|
||||
//
|
||||
// For clarity and as a general convention in the OpenVPN code,
|
||||
// any object that inherits from RC should also declare a Ptr
|
||||
// typedef that defines the smart pointer type that should be used to
|
||||
// track the object, e.g.:
|
||||
//
|
||||
// class Foo : public RC<thread_unsafe_refcount> {
|
||||
// public:
|
||||
// typedef RCPtr<Foo> Ptr; // strong pointer
|
||||
// typedef RCWeakPtr<Foo> WPtr; // weak pointer
|
||||
// };
|
||||
//
|
||||
// This allows a smart-pointer to Foo to be referred to
|
||||
// as Foo::Ptr or Foo::WPtr.
|
||||
//
|
||||
// Note that RC/RCWeak fully supports virtual inheritance. For
|
||||
// example, consider the diamond inheritance pattern below, where
|
||||
// both A and B objects contain their own reference count, but C
|
||||
// inherits from both A and B. To prevent C objects from
|
||||
// having two separate reference counts, it is necessary to
|
||||
// virtually inherit from RC.
|
||||
//
|
||||
// class A : public virtual RC<thread_unsafe_refcount> {}
|
||||
// class B : public virtual RC<thread_unsafe_refcount> {}
|
||||
// class C : public A, public B {}
|
||||
|
||||
#ifndef OPENVPN_COMMON_RC_H
|
||||
#define OPENVPN_COMMON_RC_H
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/olong.hpp>
|
||||
|
||||
#ifdef OPENVPN_RC_DEBUG
|
||||
#include <iostream>
|
||||
#include <openvpn/common/demangle.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// The smart pointer class
|
||||
template <typename T>
|
||||
class RCPtr
|
||||
{
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
RCPtr() noexcept
|
||||
: px(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
RCPtr(T* p, const bool add_ref=true) noexcept
|
||||
: px(p)
|
||||
{
|
||||
if (px && add_ref)
|
||||
intrusive_ptr_add_ref(px);
|
||||
}
|
||||
|
||||
RCPtr(const RCPtr& rhs) noexcept
|
||||
: px(rhs.px)
|
||||
{
|
||||
if (px)
|
||||
intrusive_ptr_add_ref(px);
|
||||
}
|
||||
|
||||
RCPtr(RCPtr&& rhs) noexcept
|
||||
: px(rhs.px)
|
||||
{
|
||||
rhs.px = nullptr;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RCPtr(const RCPtr<U>& rhs) noexcept
|
||||
: px(rhs.get())
|
||||
{
|
||||
if (px)
|
||||
intrusive_ptr_add_ref(px);
|
||||
}
|
||||
|
||||
~RCPtr()
|
||||
{
|
||||
if (px)
|
||||
intrusive_ptr_release(px);
|
||||
}
|
||||
|
||||
RCPtr& operator=(const RCPtr& rhs) noexcept
|
||||
{
|
||||
RCPtr(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RCPtr& operator=(RCPtr&& rhs) noexcept
|
||||
{
|
||||
RCPtr(std::move(rhs)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() noexcept
|
||||
{
|
||||
RCPtr().swap(*this);
|
||||
}
|
||||
|
||||
void reset(T* rhs) noexcept
|
||||
{
|
||||
RCPtr(rhs).swap(*this);
|
||||
}
|
||||
|
||||
void swap(RCPtr& rhs) noexcept
|
||||
{
|
||||
std::swap(px, rhs.px);
|
||||
}
|
||||
|
||||
T* get() const noexcept
|
||||
{
|
||||
return px;
|
||||
}
|
||||
|
||||
T& operator*() const noexcept
|
||||
{
|
||||
return *px;
|
||||
}
|
||||
|
||||
T* operator->() const noexcept
|
||||
{
|
||||
return px;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return px != nullptr;
|
||||
}
|
||||
|
||||
bool operator==(const RCPtr& rhs) const
|
||||
{
|
||||
return px == rhs.px;
|
||||
}
|
||||
|
||||
bool operator!=(const RCPtr& rhs) const
|
||||
{
|
||||
return px != rhs.px;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RCPtr<U> static_pointer_cast() const noexcept
|
||||
{
|
||||
return RCPtr<U>(static_cast<U*>(px));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RCPtr<U> dynamic_pointer_cast() const noexcept
|
||||
{
|
||||
return RCPtr<U>(dynamic_cast<U*>(px));
|
||||
}
|
||||
|
||||
private:
|
||||
T* px;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RCWeakPtr
|
||||
{
|
||||
typedef RCPtr<T> Strong;
|
||||
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
RCWeakPtr() noexcept {}
|
||||
|
||||
RCWeakPtr(const Strong& p) noexcept
|
||||
{
|
||||
if (p)
|
||||
controller = p->refcount_.controller;
|
||||
}
|
||||
|
||||
RCWeakPtr(T* p) noexcept
|
||||
{
|
||||
if (p)
|
||||
controller = p->refcount_.controller;
|
||||
}
|
||||
|
||||
void reset(const Strong& p) noexcept
|
||||
{
|
||||
if (p)
|
||||
controller = p->refcount_.controller;
|
||||
else
|
||||
controller.reset();
|
||||
}
|
||||
|
||||
void reset(T* p) noexcept
|
||||
{
|
||||
if (p)
|
||||
controller = p->refcount_.controller;
|
||||
else
|
||||
controller.reset();
|
||||
}
|
||||
|
||||
void reset() noexcept
|
||||
{
|
||||
controller.reset();
|
||||
}
|
||||
|
||||
void swap(RCWeakPtr& other) noexcept
|
||||
{
|
||||
controller.swap(other.controller);
|
||||
}
|
||||
|
||||
olong use_count() const noexcept
|
||||
{
|
||||
if (controller)
|
||||
return controller->use_count();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool expired() const noexcept
|
||||
{
|
||||
return use_count() == 0;
|
||||
}
|
||||
|
||||
Strong lock() const noexcept
|
||||
{
|
||||
if (controller)
|
||||
return controller->template lock<Strong>();
|
||||
else
|
||||
return Strong();
|
||||
}
|
||||
|
||||
private:
|
||||
typename T::Controller::Ptr controller;
|
||||
};
|
||||
|
||||
class thread_unsafe_refcount
|
||||
{
|
||||
public:
|
||||
thread_unsafe_refcount() noexcept
|
||||
: rc(olong(0))
|
||||
{
|
||||
}
|
||||
|
||||
void operator++() noexcept
|
||||
{
|
||||
++rc;
|
||||
}
|
||||
|
||||
olong operator--() noexcept
|
||||
{
|
||||
return --rc;
|
||||
}
|
||||
|
||||
bool inc_if_nonzero() noexcept
|
||||
{
|
||||
if (rc)
|
||||
{
|
||||
++rc;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
olong use_count() const noexcept
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
static constexpr bool is_thread_safe()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
void notify_release() noexcept
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
template <typename T>
|
||||
class ListHead
|
||||
{
|
||||
public:
|
||||
ListHead() noexcept : ptr(nullptr) {}
|
||||
|
||||
T* load() noexcept
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void insert(T* item) noexcept
|
||||
{
|
||||
item->next = ptr;
|
||||
ptr = item;
|
||||
}
|
||||
|
||||
private:
|
||||
ListHead(const ListHead&) = delete;
|
||||
ListHead& operator=(const ListHead&) = delete;
|
||||
|
||||
T* ptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
private:
|
||||
thread_unsafe_refcount(const thread_unsafe_refcount&) = delete;
|
||||
thread_unsafe_refcount& operator=(const thread_unsafe_refcount&) = delete;
|
||||
|
||||
olong rc;
|
||||
};
|
||||
|
||||
class thread_safe_refcount
|
||||
{
|
||||
public:
|
||||
thread_safe_refcount() noexcept
|
||||
: rc(olong(0))
|
||||
{
|
||||
}
|
||||
|
||||
void operator++() noexcept
|
||||
{
|
||||
rc.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
olong operator--() noexcept
|
||||
{
|
||||
// http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html
|
||||
const olong ret = rc.fetch_sub(1, std::memory_order_release) - 1;
|
||||
if (ret == 0)
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// If refcount is 0, do nothing and return false.
|
||||
// If refcount != 0, increment it and return true.
|
||||
bool inc_if_nonzero() noexcept
|
||||
{
|
||||
olong previous = rc.load(std::memory_order_relaxed);
|
||||
while (true)
|
||||
{
|
||||
if (!previous)
|
||||
break;
|
||||
if (rc.compare_exchange_weak(previous, previous + 1, std::memory_order_relaxed))
|
||||
break;
|
||||
}
|
||||
return previous > 0;
|
||||
}
|
||||
|
||||
olong use_count() const noexcept
|
||||
{
|
||||
return rc.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
static constexpr bool is_thread_safe()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
void notify_release() noexcept
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
template <typename T>
|
||||
class ListHead
|
||||
{
|
||||
public:
|
||||
ListHead() noexcept : ptr(nullptr) {}
|
||||
|
||||
T* load() noexcept
|
||||
{
|
||||
return ptr.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void insert(T* item) noexcept
|
||||
{
|
||||
T* previous = ptr.load(std::memory_order_relaxed);
|
||||
while (true)
|
||||
{
|
||||
item->next = previous;
|
||||
if (ptr.compare_exchange_weak(previous, item, std::memory_order_relaxed))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ListHead(const ListHead&) = delete;
|
||||
ListHead& operator=(const ListHead&) = delete;
|
||||
|
||||
std::atomic<T*> ptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
private:
|
||||
thread_safe_refcount(const thread_safe_refcount&) = delete;
|
||||
thread_safe_refcount& operator=(const thread_safe_refcount&) = delete;
|
||||
|
||||
std::atomic<olong> rc;
|
||||
};
|
||||
|
||||
// Reference count base class for objects tracked by RCPtr.
|
||||
// Disallows copying and assignment.
|
||||
template <typename RCImpl> // RCImpl = thread_safe_refcount or thread_unsafe_refcount
|
||||
class RC
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<RC> Ptr;
|
||||
|
||||
RC() noexcept {}
|
||||
virtual ~RC() {}
|
||||
|
||||
olong use_count() const noexcept
|
||||
{
|
||||
return refcount_.use_count();
|
||||
}
|
||||
|
||||
static constexpr bool is_thread_safe()
|
||||
{
|
||||
return RCImpl::is_thread_safe();
|
||||
}
|
||||
|
||||
private:
|
||||
RC(const RC&) = delete;
|
||||
RC& operator=(const RC&) = delete;
|
||||
|
||||
template <typename R> friend void intrusive_ptr_add_ref(R* p) noexcept;
|
||||
template <typename R> friend void intrusive_ptr_release(R* p) noexcept;
|
||||
RCImpl refcount_;
|
||||
};
|
||||
|
||||
// Like RC, but allows object to be copied and assigned.
|
||||
template <typename RCImpl> // RCImpl = thread_safe_refcount or thread_unsafe_refcount
|
||||
class RCCopyable
|
||||
{
|
||||
public:
|
||||
RCCopyable() noexcept {}
|
||||
RCCopyable(const RCCopyable&) noexcept {}
|
||||
RCCopyable& operator=(const RCCopyable&) noexcept { return *this; }
|
||||
virtual ~RCCopyable() {}
|
||||
|
||||
olong use_count() const noexcept
|
||||
{
|
||||
return refcount_.use_count();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename R> friend void intrusive_ptr_add_ref(R* p) noexcept;
|
||||
template <typename R> friend void intrusive_ptr_release(R* p) noexcept;
|
||||
RCImpl refcount_;
|
||||
};
|
||||
|
||||
// Like RC, but also allows weak pointers and release notification callables
|
||||
template <typename RCImpl> // RCImpl = thread_safe_refcount or thread_unsafe_refcount
|
||||
class RCWeak
|
||||
{
|
||||
template<typename T>
|
||||
friend class RCWeakPtr;
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
// Base class of release notification callables
|
||||
class NotifyBase
|
||||
{
|
||||
public:
|
||||
NotifyBase() noexcept {}
|
||||
virtual void call() noexcept = 0;
|
||||
virtual ~NotifyBase() {}
|
||||
NotifyBase* next;
|
||||
|
||||
private:
|
||||
NotifyBase(const NotifyBase&) = delete;
|
||||
NotifyBase& operator=(const NotifyBase&) = delete;
|
||||
};
|
||||
|
||||
// A release notification callable
|
||||
template <typename CALLABLE>
|
||||
class NotifyItem : public NotifyBase
|
||||
{
|
||||
public:
|
||||
NotifyItem(const CALLABLE& c) noexcept
|
||||
: callable(c)
|
||||
{
|
||||
}
|
||||
|
||||
NotifyItem(CALLABLE&& c) noexcept
|
||||
: callable(std::move(c))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void call() noexcept override
|
||||
{
|
||||
callable();
|
||||
}
|
||||
|
||||
CALLABLE callable;
|
||||
};
|
||||
|
||||
// Head of a linked-list of release notification callables
|
||||
class NotifyListHead
|
||||
{
|
||||
public:
|
||||
NotifyListHead() noexcept {}
|
||||
|
||||
template <typename CALLABLE>
|
||||
void add(const CALLABLE& c) noexcept
|
||||
{
|
||||
NotifyBase* item = new NotifyItem<CALLABLE>(c);
|
||||
head.insert(item);
|
||||
}
|
||||
|
||||
template <typename CALLABLE>
|
||||
void add(CALLABLE&& c) noexcept
|
||||
{
|
||||
NotifyBase* item = new NotifyItem<CALLABLE>(std::move(c));
|
||||
head.insert(item);
|
||||
}
|
||||
|
||||
void release() noexcept
|
||||
{
|
||||
// In thread-safe mode, list traversal is guaranteed to be
|
||||
// contention-free because we are not called until refcount
|
||||
// reaches zero and after a std::memory_order_acquire fence.
|
||||
NotifyBase* nb = head.load();
|
||||
while (nb)
|
||||
{
|
||||
NotifyBase* next = nb->next;
|
||||
nb->call();
|
||||
delete nb;
|
||||
nb = next;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NotifyListHead(const NotifyListHead&) = delete;
|
||||
NotifyListHead& operator=(const NotifyListHead&) = delete;
|
||||
|
||||
typename RCImpl::template ListHead<NotifyBase> head;
|
||||
};
|
||||
#endif
|
||||
|
||||
// For weak-referenceable objects, we must detach the
|
||||
// refcount from the object and place it in Controller.
|
||||
struct Controller : public RC<RCImpl>
|
||||
{
|
||||
typedef RCPtr<Controller> Ptr;
|
||||
|
||||
Controller(RCWeak* parent_arg) noexcept
|
||||
: parent(parent_arg)
|
||||
{
|
||||
}
|
||||
|
||||
olong use_count() const noexcept
|
||||
{
|
||||
return rc.use_count();
|
||||
}
|
||||
|
||||
template <typename PTR>
|
||||
PTR lock() noexcept
|
||||
{
|
||||
if (rc.inc_if_nonzero())
|
||||
return PTR(static_cast<typename PTR::element_type*>(parent), false);
|
||||
else
|
||||
return PTR();
|
||||
}
|
||||
|
||||
RCWeak *const parent; // dangles (harmlessly) after rc decrements to 0
|
||||
RCImpl rc; // refcount
|
||||
};
|
||||
|
||||
struct ControllerRef
|
||||
{
|
||||
ControllerRef(RCWeak* parent) noexcept
|
||||
: controller(new Controller(parent))
|
||||
{
|
||||
}
|
||||
|
||||
void operator++() noexcept
|
||||
{
|
||||
++controller->rc;
|
||||
}
|
||||
|
||||
olong operator--() noexcept
|
||||
{
|
||||
return --controller->rc;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
void notify_release() noexcept
|
||||
{
|
||||
notify.release();
|
||||
}
|
||||
#endif
|
||||
|
||||
typename Controller::Ptr controller; // object containing actual refcount
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
NotifyListHead notify; // linked list of callables to be notified on object release
|
||||
#endif
|
||||
};
|
||||
|
||||
public:
|
||||
typedef RCPtr<RCWeak> Ptr;
|
||||
typedef RCWeakPtr<RCWeak> WPtr;
|
||||
|
||||
RCWeak() noexcept
|
||||
: refcount_(this)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RCWeak()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
// Add observers to be called just prior to object deletion,
|
||||
// but after refcount has been decremented to 0. At this
|
||||
// point, all weak pointers have expired, and no strong
|
||||
// pointers are outstanding. Callables can access the
|
||||
// object by raw pointer but must NOT attempt to create a
|
||||
// strong pointer referencing the object.
|
||||
|
||||
template <typename CALLABLE>
|
||||
void rc_release_notify(const CALLABLE& c) noexcept
|
||||
{
|
||||
refcount_.notify.add(c);
|
||||
}
|
||||
|
||||
template <typename CALLABLE>
|
||||
void rc_release_notify(CALLABLE&& c) noexcept
|
||||
{
|
||||
refcount_.notify.add(std::move(c));
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
RCWeak(const RCWeak&) = delete;
|
||||
RCWeak& operator=(const RCWeak&) = delete;
|
||||
|
||||
template <typename R> friend void intrusive_ptr_add_ref(R* p) noexcept;
|
||||
template <typename R> friend void intrusive_ptr_release(R* p) noexcept;
|
||||
|
||||
ControllerRef refcount_;
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
inline void intrusive_ptr_add_ref(R *p) noexcept
|
||||
{
|
||||
#ifdef OPENVPN_RC_DEBUG
|
||||
std::cout << "ADD REF " << cxx_demangle(typeid(p).name()) << std::endl;
|
||||
#endif
|
||||
++p->refcount_;
|
||||
}
|
||||
|
||||
template <typename R>
|
||||
inline void intrusive_ptr_release(R *p) noexcept
|
||||
{
|
||||
if (--p->refcount_ == 0)
|
||||
{
|
||||
#ifdef OPENVPN_RC_DEBUG
|
||||
std::cout << "DEL OBJ " << cxx_demangle(typeid(p).name()) << std::endl;
|
||||
#endif
|
||||
#ifdef OPENVPN_RC_NOTIFY
|
||||
p->refcount_.notify_release();
|
||||
#endif
|
||||
delete p;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef OPENVPN_RC_DEBUG
|
||||
std::cout << "REL REF " << cxx_demangle(typeid(p).name()) << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_RC_H
|
||||
@@ -0,0 +1,310 @@
|
||||
// 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 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_COMMON_REDIR_H
|
||||
#define OPENVPN_COMMON_REDIR_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/tempfile.hpp>
|
||||
#include <openvpn/common/pipe.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct RedirectBase
|
||||
{
|
||||
OPENVPN_EXCEPTION(redirect_std_err);
|
||||
virtual void redirect() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual ~RedirectBase() {}
|
||||
};
|
||||
|
||||
struct RedirectStdFD : public RedirectBase
|
||||
{
|
||||
virtual void redirect() noexcept override
|
||||
{
|
||||
// stdin
|
||||
if (in.defined())
|
||||
{
|
||||
::dup2(in(), 0);
|
||||
if (in() <= 2)
|
||||
in.release();
|
||||
}
|
||||
|
||||
// stdout
|
||||
if (out.defined())
|
||||
{
|
||||
::dup2(out(), 1);
|
||||
if (!err.defined() && combine_out_err)
|
||||
::dup2(out(), 2);
|
||||
if (out() <= 2)
|
||||
out.release();
|
||||
}
|
||||
|
||||
// stderr
|
||||
if (err.defined())
|
||||
{
|
||||
::dup2(err(), 2);
|
||||
if (err() <= 2)
|
||||
err.release();
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
virtual void close() override
|
||||
{
|
||||
in.close();
|
||||
out.close();
|
||||
err.close();
|
||||
}
|
||||
|
||||
ScopedFD in;
|
||||
ScopedFD out;
|
||||
ScopedFD err;
|
||||
bool combine_out_err = false;
|
||||
};
|
||||
|
||||
class RedirectNull : public RedirectStdFD
|
||||
{
|
||||
public:
|
||||
RedirectNull()
|
||||
{
|
||||
// open /dev/null for stdin
|
||||
in.reset(::open("/dev/null", O_RDONLY, 0));
|
||||
if (!in.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "RedirectNull: error opening /dev/null for input : " << strerror_str(eno));
|
||||
}
|
||||
|
||||
// open /dev/null for stdout
|
||||
out.reset(::open("/dev/null", O_RDWR, 0));
|
||||
if (!out.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "RedirectNull: error opening /dev/null for output : " << strerror_str(eno));
|
||||
}
|
||||
combine_out_err = true;
|
||||
}
|
||||
};
|
||||
|
||||
class RedirectStd : public RedirectStdFD
|
||||
{
|
||||
public:
|
||||
// flags shortcuts
|
||||
static constexpr int FLAGS_OVERWRITE = O_CREAT | O_WRONLY | O_TRUNC;
|
||||
static constexpr int FLAGS_APPEND = O_CREAT | O_WRONLY | O_APPEND;
|
||||
static constexpr int FLAGS_MUST_NOT_EXIST = O_CREAT | O_WRONLY | O_EXCL;
|
||||
|
||||
// mode shortcuts
|
||||
static constexpr mode_t MODE_ALL = 0777;
|
||||
static constexpr mode_t MODE_USER_GROUP = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
|
||||
static constexpr mode_t MODE_USER = S_IRUSR | S_IWUSR;
|
||||
|
||||
RedirectStd(const std::string& in_fn,
|
||||
const std::string& out_fn,
|
||||
const int out_flags = FLAGS_OVERWRITE,
|
||||
const mode_t out_mode = MODE_ALL,
|
||||
const bool combine_out_err_arg = true)
|
||||
{
|
||||
if (!in_fn.empty())
|
||||
open_input(in_fn);
|
||||
open_output(out_fn, out_flags, out_mode);
|
||||
combine_out_err = combine_out_err_arg;
|
||||
}
|
||||
|
||||
protected:
|
||||
RedirectStd() {}
|
||||
|
||||
void open_input(const std::string& fn)
|
||||
{
|
||||
// open input file for stdin
|
||||
in.reset(::open(fn.c_str(), O_RDONLY, 0));
|
||||
if (!in.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "error opening input file: " << fn << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
|
||||
void open_output(const std::string& fn,
|
||||
const int flags,
|
||||
const mode_t mode)
|
||||
{
|
||||
// open output file for stdout/stderr
|
||||
out.reset(::open(fn.c_str(),
|
||||
flags,
|
||||
mode));
|
||||
if (!out.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "error opening output file: " << fn << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class RedirectTemp : public RedirectStd
|
||||
{
|
||||
public:
|
||||
RedirectTemp(const std::string& stdin_fn,
|
||||
TempFile& stdout_temp,
|
||||
const bool combine_out_err_arg)
|
||||
{
|
||||
open_input(stdin_fn);
|
||||
out = std::move(stdout_temp.fd);
|
||||
combine_out_err = combine_out_err_arg;
|
||||
}
|
||||
|
||||
RedirectTemp(const std::string& stdin_fn,
|
||||
TempFile& stdout_temp,
|
||||
TempFile& stderr_temp)
|
||||
{
|
||||
open_input(stdin_fn);
|
||||
out = std::move(stdout_temp.fd);
|
||||
err = std::move(stderr_temp.fd);
|
||||
}
|
||||
};
|
||||
|
||||
class RedirectPipe : public RedirectStdFD
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
COMBINE_OUT_ERR = (1<<0), // capture combined stdout/stderr using a pipe
|
||||
ENABLE_IN = (1<<1), // make a string -> stdin pipe, otherwise redirect stdin from /dev/null
|
||||
IGNORE_IN = (1<<2), // don't touch stdin
|
||||
IGNORE_OUT = (1<<3), // don't touch stdout
|
||||
IGNORE_ERR = (1<<4), // don't touch stderr
|
||||
};
|
||||
|
||||
struct InOut
|
||||
{
|
||||
std::string in;
|
||||
std::string out;
|
||||
std::string err;
|
||||
};
|
||||
|
||||
RedirectPipe() {}
|
||||
|
||||
RedirectPipe(RedirectStdFD& remote,
|
||||
const unsigned int flags_arg)
|
||||
: flags(flags_arg)
|
||||
{
|
||||
// stdout
|
||||
if (!(flags & IGNORE_OUT))
|
||||
{
|
||||
int fd[2];
|
||||
Pipe::make_pipe(fd);
|
||||
out.reset(cloexec(fd[0]));
|
||||
remote.out.reset(fd[1]);
|
||||
}
|
||||
|
||||
// stderr
|
||||
if (!(flags & IGNORE_ERR))
|
||||
{
|
||||
combine_out_err = remote.combine_out_err = ((flags & (COMBINE_OUT_ERR|IGNORE_OUT)) == COMBINE_OUT_ERR);
|
||||
if (!combine_out_err)
|
||||
{
|
||||
int fd[2];
|
||||
Pipe::make_pipe(fd);
|
||||
err.reset(cloexec(fd[0]));
|
||||
remote.err.reset(fd[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// stdin
|
||||
if (!(flags & IGNORE_IN))
|
||||
{
|
||||
if (flags & ENABLE_IN)
|
||||
{
|
||||
int fd[2];
|
||||
Pipe::make_pipe(fd);
|
||||
in.reset(cloexec(fd[1]));
|
||||
remote.in.reset(fd[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// open /dev/null for stdin
|
||||
remote.in.reset(::open("/dev/null", O_RDONLY, 0));
|
||||
if (!remote.in.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "error opening /dev/null : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transact(InOut& inout)
|
||||
{
|
||||
openvpn_io::io_context io_context(1);
|
||||
|
||||
std::unique_ptr<Pipe::SD_OUT> send_in;
|
||||
std::unique_ptr<Pipe::SD_IN> recv_out;
|
||||
std::unique_ptr<Pipe::SD_IN> recv_err;
|
||||
|
||||
if (!(flags & IGNORE_IN))
|
||||
send_in.reset(new Pipe::SD_OUT(io_context, inout.in, in));
|
||||
if (!(flags & IGNORE_OUT))
|
||||
recv_out.reset(new Pipe::SD_IN(io_context, out));
|
||||
if (!(flags & IGNORE_ERR))
|
||||
recv_err.reset(new Pipe::SD_IN(io_context, err));
|
||||
|
||||
io_context.run();
|
||||
|
||||
if (recv_out)
|
||||
inout.out = recv_out->content();
|
||||
if (recv_err)
|
||||
inout.err = recv_err->content();
|
||||
}
|
||||
|
||||
private:
|
||||
// set FD_CLOEXEC to prevent fd from being passed across execs
|
||||
static int cloexec(const int fd)
|
||||
{
|
||||
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "error setting FD_CLOEXEC on pipe : " << strerror_str(eno));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
const unsigned int flags = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,441 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Manage a pool of threads for a multi-threaded server.
|
||||
//
|
||||
// To stress test this code, in client after serv->start() add:
|
||||
// if (unit == 3 || unit == 5)
|
||||
// throw Exception("HIT IT");
|
||||
// And after "case PThreadBarrier::ERROR:"
|
||||
// if (unit & 1)
|
||||
// break;
|
||||
|
||||
#ifndef OPENVPN_COMMON_RUNCONTEXT_H
|
||||
#define OPENVPN_COMMON_RUNCONTEXT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <type_traits> // for std::is_nothrow_move_constructible
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/signal.hpp>
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/common/environ.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/signal_name.hpp>
|
||||
#include <openvpn/asio/asiosignal.hpp>
|
||||
#include <openvpn/time/time.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/time/timestr.hpp>
|
||||
#include <openvpn/common/logsetup.hpp>
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct RunContextLogEntry
|
||||
{
|
||||
RunContextLogEntry(const time_t timestamp_arg, const std::string& text_arg)
|
||||
: timestamp(timestamp_arg),
|
||||
text(text_arg)
|
||||
{
|
||||
}
|
||||
|
||||
time_t timestamp;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
template <typename RC_TYPE>
|
||||
struct ServerThreadType : public virtual RC_TYPE
|
||||
{
|
||||
typedef RCPtr<ServerThreadType> Ptr;
|
||||
typedef RCWeakPtr<ServerThreadType> WPtr;
|
||||
|
||||
virtual void thread_safe_stop() = 0;
|
||||
|
||||
virtual void log_notify(const RunContextLogEntry& le)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef ServerThreadType<RCWeak<thread_safe_refcount>> ServerThreadWeakBase;
|
||||
typedef ServerThreadType<RC<thread_safe_refcount>> ServerThreadBase;
|
||||
|
||||
struct RunContextBase : public LogBase
|
||||
{
|
||||
virtual void cancel() = 0;
|
||||
virtual std::vector<RunContextLogEntry> add_log_observer(const unsigned int unit) = 0;
|
||||
virtual void disable_log_history() = 0;
|
||||
virtual Stop* async_stop() = 0;
|
||||
};
|
||||
|
||||
template <typename ServerThread, typename Stats>
|
||||
class RunContext : public RunContextBase
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<RunContext> Ptr;
|
||||
|
||||
class ThreadContext
|
||||
{
|
||||
public:
|
||||
ThreadContext(RunContext& ctx_arg)
|
||||
: ctx(ctx_arg)
|
||||
{
|
||||
ctx.add_thread();
|
||||
}
|
||||
|
||||
~ThreadContext()
|
||||
{
|
||||
ctx.remove_thread();
|
||||
}
|
||||
|
||||
private:
|
||||
RunContext& ctx;
|
||||
};
|
||||
|
||||
RunContext()
|
||||
: exit_timer(io_context),
|
||||
log_context(this),
|
||||
log_wrap()
|
||||
{
|
||||
signals.reset(new ASIOSignals(io_context));
|
||||
signal_rearm();
|
||||
schedule_debug_exit();
|
||||
}
|
||||
|
||||
void set_async_stop(Stop* async_stop)
|
||||
{
|
||||
async_stop_ = async_stop;
|
||||
}
|
||||
|
||||
void set_log_reopen(LogSetup::Ptr lr)
|
||||
{
|
||||
log_reopen = std::move(lr);
|
||||
}
|
||||
|
||||
void set_thread(const unsigned int unit, std::thread* thread)
|
||||
{
|
||||
while (threadlist.size() <= unit)
|
||||
threadlist.push_back(nullptr);
|
||||
if (threadlist[unit])
|
||||
throw Exception("RunContext::set_thread: overwrite");
|
||||
threadlist[unit] = thread;
|
||||
}
|
||||
|
||||
// called from worker thread
|
||||
void set_server(const unsigned int unit, ServerThread* serv)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (halt)
|
||||
throw Exception("RunContext::set_server: halting");
|
||||
while (servlist.size() <= unit)
|
||||
servlist.push_back(nullptr);
|
||||
if (servlist[unit])
|
||||
throw Exception("RunContext::set_server: overwrite");
|
||||
servlist[unit] = serv;
|
||||
}
|
||||
|
||||
// called from worker thread
|
||||
void clear_server(const unsigned int unit)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (unit < servlist.size())
|
||||
servlist[unit] = nullptr;
|
||||
|
||||
// remove log observer entry, if present
|
||||
auto lu = std::find(log_observers.begin(), log_observers.end(), unit);
|
||||
if (lu != log_observers.end())
|
||||
log_observers.erase(lu);
|
||||
}
|
||||
|
||||
std::vector<typename ServerThread::Ptr> get_servers()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
std::vector<typename ServerThread::Ptr> ret;
|
||||
if (halt)
|
||||
return ret;
|
||||
ret.reserve(servlist.size());
|
||||
for (auto sp : servlist)
|
||||
ret.emplace_back(sp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void enable_log_history()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (!log_history)
|
||||
log_history.reset(new std::vector<RunContextLogEntry>());
|
||||
}
|
||||
|
||||
virtual void disable_log_history() override
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
log_history.reset();
|
||||
}
|
||||
|
||||
virtual std::vector<RunContextLogEntry> add_log_observer(const unsigned int unit) override
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
auto lu = std::find(log_observers.begin(), log_observers.end(), unit);
|
||||
if (lu == log_observers.end())
|
||||
log_observers.push_back(unit);
|
||||
if (log_history)
|
||||
return *log_history;
|
||||
else
|
||||
return std::vector<RunContextLogEntry>();
|
||||
}
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
void set_exit_socket(ScopedFD& fd)
|
||||
{
|
||||
exit_sock.reset(new openvpn_io::posix::stream_descriptor(io_context, fd.release()));
|
||||
exit_sock->async_read_some(openvpn_io::null_buffers(),
|
||||
[self=Ptr(this)](const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
{
|
||||
if (!error)
|
||||
self->cancel();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_prefix(const std::string& pre)
|
||||
{
|
||||
prefix = pre + ": ";
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (!halt)
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
void join()
|
||||
{
|
||||
for (size_t i = 0; i < threadlist.size(); ++i)
|
||||
{
|
||||
std::thread* t = threadlist[i];
|
||||
if (t)
|
||||
{
|
||||
t->join();
|
||||
delete t;
|
||||
threadlist[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void log(const std::string& str) override
|
||||
{
|
||||
time_t now;
|
||||
const std::string ts = date_time_store_time_t(now);
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
std::cout << ts << ' ' << str << std::flush;
|
||||
|
||||
if (!log_observers.empty() || log_history)
|
||||
{
|
||||
const RunContextLogEntry le(now, str);
|
||||
for (auto &si : log_observers)
|
||||
{
|
||||
ServerThread* st = servlist[si];
|
||||
if (st)
|
||||
st->log_notify(le);
|
||||
}
|
||||
if (log_history)
|
||||
log_history->emplace_back(now, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called from main or worker thread
|
||||
virtual void cancel() override
|
||||
{
|
||||
if (halt)
|
||||
return;
|
||||
openvpn_io::post(io_context, [self=Ptr(this)]()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(self->mutex);
|
||||
if (self->halt)
|
||||
return;
|
||||
self->halt = true;
|
||||
|
||||
// async stop
|
||||
if (self->async_stop_)
|
||||
self->async_stop_->stop();
|
||||
|
||||
self->exit_timer.cancel();
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
self->exit_sock.reset();
|
||||
#endif
|
||||
if (self->signals)
|
||||
self->signals->cancel();
|
||||
|
||||
// stop threads
|
||||
{
|
||||
unsigned int stopped = 0;
|
||||
for (size_t i = 0; i < self->servlist.size(); ++i)
|
||||
{
|
||||
ServerThread* serv = self->servlist[i];
|
||||
if (serv)
|
||||
{
|
||||
serv->thread_safe_stop();
|
||||
++stopped;
|
||||
}
|
||||
self->servlist[i] = nullptr;
|
||||
}
|
||||
OPENVPN_LOG(self->prefix << "Stopping " << stopped << '/' << self->servlist.size() << " thread(s)");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const Log::Context::Wrapper& log_wrapper() { return log_wrap; }
|
||||
|
||||
void set_stats_obj(const typename Stats::Ptr& stats_arg)
|
||||
{
|
||||
stats = stats_arg;
|
||||
}
|
||||
|
||||
virtual Stop* async_stop() override
|
||||
{
|
||||
return async_stop_;
|
||||
}
|
||||
|
||||
private:
|
||||
// called from main or worker thread
|
||||
void add_thread()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
++thread_count;
|
||||
}
|
||||
|
||||
// called from main or worker thread
|
||||
void remove_thread()
|
||||
{
|
||||
bool last = false;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
last = (--thread_count <= 0);
|
||||
}
|
||||
if (last)
|
||||
cancel();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void signal(const openvpn_io::error_code& error, int signum)
|
||||
{
|
||||
if (!error && !halt)
|
||||
{
|
||||
OPENVPN_LOG("ASIO SIGNAL: " << signal_name(signum));
|
||||
switch (signum)
|
||||
{
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
cancel();
|
||||
break;
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
case SIGUSR2:
|
||||
if (stats)
|
||||
OPENVPN_LOG(stats->dump());
|
||||
signal_rearm();
|
||||
break;
|
||||
case SIGHUP:
|
||||
if (log_reopen)
|
||||
log_reopen->reopen();
|
||||
signal_rearm();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
signal_rearm();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void signal_rearm()
|
||||
{
|
||||
signals->register_signals_all([self=Ptr(this)](const openvpn_io::error_code& error, int signal_number)
|
||||
{
|
||||
self->signal(error, signal_number);
|
||||
});
|
||||
}
|
||||
|
||||
// debugging feature -- exit in n seconds
|
||||
void schedule_debug_exit()
|
||||
{
|
||||
const std::string exit_in = Environ::find_static("EXIT_IN");
|
||||
if (exit_in.empty())
|
||||
return;
|
||||
const unsigned int n_sec = parse_number_throw<unsigned int>(exit_in, "error parsing EXIT_IN");
|
||||
exit_timer.expires_after(Time::Duration::seconds(n_sec));
|
||||
exit_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
if (error || self->halt)
|
||||
return;
|
||||
OPENVPN_LOG("DEBUG EXIT");
|
||||
self->cancel();
|
||||
});
|
||||
}
|
||||
|
||||
// these vars only used by main thread
|
||||
openvpn_io::io_context io_context{1};
|
||||
typename Stats::Ptr stats;
|
||||
ASIOSignals::Ptr signals;
|
||||
AsioTimer exit_timer;
|
||||
std::string prefix;
|
||||
std::vector<std::thread*> threadlist;
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
std::unique_ptr<openvpn_io::posix::stream_descriptor> exit_sock;
|
||||
#endif
|
||||
|
||||
// main lock
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
// servlist and related vars protected by mutex
|
||||
std::vector<ServerThread*> servlist;
|
||||
int thread_count = 0;
|
||||
|
||||
// stop
|
||||
Stop* async_stop_ = nullptr;
|
||||
|
||||
// log observers
|
||||
std::vector<unsigned int> log_observers; // unit numbers of log observers
|
||||
std::unique_ptr<std::vector<RunContextLogEntry>> log_history;
|
||||
|
||||
// logging
|
||||
Log::Context log_context;
|
||||
Log::Context::Wrapper log_wrap; // must be constructed after log_context
|
||||
LogSetup::Ptr log_reopen;
|
||||
|
||||
protected:
|
||||
volatile bool halt = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A scoped file descriptor that is automatically closed by its destructor.
|
||||
|
||||
#ifndef OPENVPN_COMMON_SCOPED_FD_H
|
||||
#define OPENVPN_COMMON_SCOPED_FD_H
|
||||
|
||||
#include <unistd.h> // for close()
|
||||
#include <errno.h>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ScopedFD
|
||||
{
|
||||
ScopedFD(const ScopedFD&) = delete;
|
||||
ScopedFD& operator=(const ScopedFD&) = delete;
|
||||
|
||||
public:
|
||||
typedef int base_type;
|
||||
|
||||
ScopedFD() : fd(undefined()) {}
|
||||
|
||||
explicit ScopedFD(const int fd_arg)
|
||||
: fd(fd_arg) {}
|
||||
|
||||
static int undefined() { return -1; }
|
||||
|
||||
int release()
|
||||
{
|
||||
const int ret = fd;
|
||||
fd = -1;
|
||||
//OPENVPN_LOG("**** SFD RELEASE=" << ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool defined_static(int fd)
|
||||
{
|
||||
return fd >= 0;
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return defined_static(fd);
|
||||
}
|
||||
|
||||
int operator()() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
void reset(const int fd_arg)
|
||||
{
|
||||
close();
|
||||
fd = fd_arg;
|
||||
//OPENVPN_LOG("**** SFD RESET=" << fd);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// unusual semantics: replace fd without closing it first
|
||||
void replace(const int fd_arg)
|
||||
{
|
||||
//OPENVPN_LOG("**** SFD REPLACE " << fd << " -> " << fd_arg);
|
||||
fd = fd_arg;
|
||||
}
|
||||
|
||||
// return false if close error
|
||||
bool close()
|
||||
{
|
||||
return close_with_errno() == 0;
|
||||
}
|
||||
|
||||
// return errno value if close error, otherwise return 0
|
||||
int close_with_errno()
|
||||
{
|
||||
int eno = 0;
|
||||
if (defined())
|
||||
{
|
||||
if (::close(fd) == -1)
|
||||
eno = errno;
|
||||
post_close(eno);
|
||||
//OPENVPN_LOG("**** SFD CLOSE fd=" << fd << " errno=" << eno);
|
||||
fd = -1;
|
||||
}
|
||||
return eno;
|
||||
}
|
||||
|
||||
virtual void post_close(const int close_errno)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ScopedFD()
|
||||
{
|
||||
//OPENVPN_LOG("**** SFD DESTRUCTOR");
|
||||
close();
|
||||
}
|
||||
|
||||
ScopedFD(ScopedFD&& other) noexcept
|
||||
{
|
||||
fd = other.fd;
|
||||
other.fd = -1;
|
||||
}
|
||||
|
||||
ScopedFD& operator=(ScopedFD&& other) noexcept
|
||||
{
|
||||
close();
|
||||
fd = other.fd;
|
||||
other.fd = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_SCOPED_FD_H
|
||||
@@ -0,0 +1,200 @@
|
||||
// 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 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/>.
|
||||
|
||||
// A general-purpose Session ID class
|
||||
|
||||
#ifndef OPENVPN_COMMON_SESS_ID_H
|
||||
#define OPENVPN_COMMON_SESS_ID_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cstdint> // for std::uint8_t, std::uint64_t
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/common/ostream.hpp>
|
||||
#include <openvpn/common/base64.hpp>
|
||||
#include <openvpn/common/arraysize.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
template <size_t SIZE>
|
||||
class SessionIDType
|
||||
{
|
||||
public:
|
||||
template <size_t S> friend class SessionIDType;
|
||||
|
||||
// Create a zeroed Sesson ID.
|
||||
SessionIDType()
|
||||
{
|
||||
// compile-time size constraints
|
||||
static_assert(sizeof(u.data) >= sizeof(std::uint64_t), "SessionIDType SIZE too small");
|
||||
static_assert(SIZE % sizeof(std::uint64_t) == size_t(0), "SessionIDType SIZE must be an integer multiple of 64 bits");
|
||||
std::memset(u.data, 0, sizeof(u.data));
|
||||
}
|
||||
|
||||
// Create a random Session ID.
|
||||
explicit SessionIDType(RandomAPI& rng, const bool allow_noncrypto_rng=false)
|
||||
{
|
||||
if (!allow_noncrypto_rng)
|
||||
rng.assert_crypto();
|
||||
rng.rand_bytes(u.data, sizeof(u.data));
|
||||
}
|
||||
|
||||
// Create a Session ID from a base64 (URL-safe) string.
|
||||
explicit SessionIDType(const std::string& b64)
|
||||
{
|
||||
Buffer srcbuf(u.data, sizeof(u.data), false);
|
||||
try {
|
||||
base64_urlsafe->decode(srcbuf, b64);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw Exception("SessionID: base64 decode: " + std::string(e.what()));
|
||||
}
|
||||
if (srcbuf.size() != sizeof(u.data))
|
||||
throw Exception("SessionID: wrong input size, actual=" + std::to_string(srcbuf.size()) + " expected=" + std::to_string(sizeof(u.data)));
|
||||
}
|
||||
|
||||
// Create a Session ID from a byte string of size size().
|
||||
explicit SessionIDType(const std::uint8_t* bytes)
|
||||
{
|
||||
std::memcpy(u.data, bytes, SIZE);
|
||||
}
|
||||
|
||||
// Create a Session ID from another Session ID of possibly
|
||||
// different size. If the other Session ID is larger,
|
||||
// truncate, if it's smaller, zero our tail.
|
||||
template <size_t S>
|
||||
explicit SessionIDType(const SessionIDType<S>& other)
|
||||
{
|
||||
for (size_t i = 0; i < array_size(u.dataz); ++i)
|
||||
u.dataz[i] = (i < array_size(other.u.dataz)) ? other.u.dataz[i] : 0;
|
||||
}
|
||||
|
||||
// Create an encrypted Session ID.
|
||||
// Intended to be used with TokenEncrypt.
|
||||
template <typename CRYPT>
|
||||
explicit SessionIDType(const SessionIDType& other, CRYPT& crypt)
|
||||
{
|
||||
crypt(u.data, other.u.data, SIZE);
|
||||
}
|
||||
|
||||
// Session ID is considered to be undefined if all bits are zero.
|
||||
bool defined() const
|
||||
{
|
||||
for (size_t i = 0; i < array_size(u.dataz); ++i)
|
||||
if (u.dataz[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the lower 64 bits of Session ID regardless of the size.
|
||||
std::uint64_t shortform() const
|
||||
{
|
||||
return u.dataz[0];
|
||||
}
|
||||
|
||||
template <typename HASH>
|
||||
void hash(HASH& h) const
|
||||
{
|
||||
h(u.dataz[0]);
|
||||
}
|
||||
|
||||
// Use a URL-safe base64 encoding.
|
||||
std::string to_string() const
|
||||
{
|
||||
return base64_urlsafe->encode(u.data, sizeof(u.data));
|
||||
}
|
||||
|
||||
bool operator==(const SessionIDType& other) const
|
||||
{
|
||||
return std::memcmp(u.data, other.u.data, sizeof(u.data)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const SessionIDType& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool operator<(const SessionIDType& other) const
|
||||
{
|
||||
return std::memcmp(u.data, other.u.data, sizeof(u.data)) < 0;
|
||||
}
|
||||
|
||||
// Weak equality means that the lower 64 bits compare equal.
|
||||
template <size_t S>
|
||||
bool eq_weak(const SessionIDType<S>& other) const
|
||||
{
|
||||
return shortform() == other.shortform();
|
||||
}
|
||||
|
||||
// True if the string looks like a Session ID.
|
||||
static bool is(const std::string& str)
|
||||
{
|
||||
return base64_urlsafe->is_base64(str, SIZE);
|
||||
}
|
||||
|
||||
static constexpr size_t size()
|
||||
{
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
const std::uint8_t* c_data() const
|
||||
{
|
||||
return u.data;
|
||||
}
|
||||
|
||||
// Find an element in an unordered map (keyed by Session ID)
|
||||
// using weak equality. If conflict is true, only return
|
||||
// element that is present by weak equality, but which is
|
||||
// not equal to *this by strong equality.
|
||||
template <typename UNORDERED_MAP>
|
||||
const SessionIDType* find_weak(const UNORDERED_MAP& m, const bool conflict) const
|
||||
{
|
||||
const size_t bi = m.bucket(*this);
|
||||
for (auto i = m.cbegin(bi); i != m.cend(bi); ++i)
|
||||
if (shortform() == i->first.shortform() && (!conflict || *this != i->first))
|
||||
return &i->first;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
std::uint64_t dataz[SIZE / sizeof(std::uint64_t)];
|
||||
std::uint8_t data[SIZE];
|
||||
} u;
|
||||
};
|
||||
|
||||
// Create two concrete types: 64 and 128-bit Session IDs.
|
||||
typedef SessionIDType<8> SessionID64;
|
||||
typedef SessionIDType<16> SessionID128;
|
||||
|
||||
OPENVPN_OSTREAM(SessionID64, to_string);
|
||||
OPENVPN_OSTREAM(SessionID128, to_string);
|
||||
}
|
||||
|
||||
OPENVPN_HASH_METHOD(openvpn::SessionID64, shortform);
|
||||
OPENVPN_HASH_METHOD(openvpn::SessionID128, shortform);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 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_COMMON_SIGNAL_H
|
||||
#define OPENVPN_COMMON_SIGNAL_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class Signal
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(signal_error);
|
||||
|
||||
typedef void (*handler_t)(int signum);
|
||||
|
||||
enum {
|
||||
F_SIGINT = (1<<0),
|
||||
F_SIGTERM = (1<<1),
|
||||
F_SIGHUP = (1<<2),
|
||||
F_SIGUSR1 = (1<<3),
|
||||
F_SIGUSR2 = (1<<4),
|
||||
F_SIGPIPE = (1<<5),
|
||||
};
|
||||
|
||||
Signal(const handler_t handler, const unsigned int flags)
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART; // restart functions if interrupted by handler
|
||||
sigconf(sa, flags_ = flags);
|
||||
}
|
||||
|
||||
~Signal()
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigconf(sa, flags_);
|
||||
}
|
||||
|
||||
private:
|
||||
static void sigconf(struct sigaction& sa, const unsigned int flags)
|
||||
{
|
||||
if (flags & F_SIGINT)
|
||||
sigact(sa, SIGINT);
|
||||
if (flags & F_SIGTERM)
|
||||
sigact(sa, SIGTERM);
|
||||
if (flags & F_SIGHUP)
|
||||
sigact(sa, SIGHUP);
|
||||
if (flags & F_SIGUSR1)
|
||||
sigact(sa, SIGUSR1);
|
||||
if (flags & F_SIGUSR2)
|
||||
sigact(sa, SIGUSR2);
|
||||
if (flags & F_SIGPIPE)
|
||||
sigact(sa, SIGPIPE);
|
||||
}
|
||||
|
||||
static void sigact(struct sigaction& sa, const int sig)
|
||||
{
|
||||
if (sigaction(sig, &sa, nullptr) == -1)
|
||||
throw signal_error();
|
||||
}
|
||||
|
||||
unsigned int flags_;
|
||||
};
|
||||
|
||||
// Like Asio posix_signal_blocker, but only block certain signals
|
||||
class SignalBlocker
|
||||
{
|
||||
SignalBlocker(const SignalBlocker&) = delete;
|
||||
SignalBlocker& operator=(const SignalBlocker&) = delete;
|
||||
|
||||
public:
|
||||
SignalBlocker(const unsigned int flags) // use signal mask from class Signal
|
||||
: blocked_(false)
|
||||
{
|
||||
sigset_t new_mask;
|
||||
sigemptyset(&new_mask);
|
||||
if (flags & Signal::F_SIGINT)
|
||||
sigaddset(&new_mask, SIGINT);
|
||||
if (flags & Signal::F_SIGTERM)
|
||||
sigaddset(&new_mask, SIGTERM);
|
||||
if (flags & Signal::F_SIGHUP)
|
||||
sigaddset(&new_mask, SIGHUP);
|
||||
if (flags & Signal::F_SIGUSR1)
|
||||
sigaddset(&new_mask, SIGUSR1);
|
||||
if (flags & Signal::F_SIGUSR2)
|
||||
sigaddset(&new_mask, SIGUSR2);
|
||||
if (flags & Signal::F_SIGPIPE)
|
||||
sigaddset(&new_mask, SIGPIPE);
|
||||
blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0);
|
||||
}
|
||||
|
||||
// Destructor restores the previous signal mask.
|
||||
~SignalBlocker()
|
||||
{
|
||||
if (blocked_)
|
||||
pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
// Have signals been blocked.
|
||||
bool blocked_;
|
||||
|
||||
// The previous signal mask.
|
||||
sigset_t old_mask_;
|
||||
};
|
||||
|
||||
// Like SignalBlocker, but block specific signals in default constructor
|
||||
struct SignalBlockerDefault : public SignalBlocker
|
||||
{
|
||||
SignalBlockerDefault()
|
||||
: SignalBlocker( // these signals should be handled by parent thread
|
||||
Signal::F_SIGINT|
|
||||
Signal::F_SIGTERM|
|
||||
Signal::F_SIGHUP|
|
||||
Signal::F_SIGUSR1|
|
||||
Signal::F_SIGUSR2|
|
||||
Signal::F_SIGPIPE)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct SignalBlockerPipe : public SignalBlocker
|
||||
{
|
||||
SignalBlockerPipe()
|
||||
: SignalBlocker(Signal::F_SIGPIPE)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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-2019 OpenVPN 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
inline std::string signal_name(const int signum)
|
||||
{
|
||||
switch (signum)
|
||||
{
|
||||
case SIGINT:
|
||||
return "SIGINT";
|
||||
case SIGTERM:
|
||||
return "SIGTERM";
|
||||
case SIGHUP:
|
||||
return "SIGHUP";
|
||||
case SIGUSR1:
|
||||
return "SIGUSR1";
|
||||
case SIGUSR2:
|
||||
return "SIGUSR2";
|
||||
case SIGPIPE:
|
||||
return "SIGPIPE";
|
||||
default:
|
||||
return std::to_string(signum);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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 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/>.
|
||||
|
||||
// define very basic types such as size_t, ssize_t
|
||||
|
||||
#ifndef OPENVPN_COMMON_SIZE_H
|
||||
#define OPENVPN_COMMON_SIZE_H
|
||||
|
||||
#include <cstddef> // defines std::size_t
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#define _SSIZE_T_
|
||||
#define _SSIZE_T_DEFINED
|
||||
#endif
|
||||
#else
|
||||
#include <unistd.h> // get ssize_t
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Portable millisecond sleep
|
||||
|
||||
#ifndef OPENVPN_COMMON_SLEEP_H
|
||||
#define OPENVPN_COMMON_SLEEP_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
inline bool sleep_milliseconds(const unsigned int milliseconds)
|
||||
{
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
::Sleep(milliseconds);
|
||||
return true;
|
||||
#else
|
||||
struct timespec ts;
|
||||
ts.tv_sec = milliseconds / 1000U;
|
||||
ts.tv_nsec = (milliseconds % 1000U) * 1000000U;
|
||||
return ::nanosleep(&ts, nullptr) == 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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 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_COMMON_SOCKOPT_H
|
||||
#define OPENVPN_COMMON_SOCKOPT_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#if !defined(OPENVPN_PLATFORM_WIN)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace SockOpt {
|
||||
|
||||
#ifdef SO_REUSEPORT
|
||||
// set SO_REUSEPORT for inter-thread load balancing
|
||||
inline void reuseport(const int fd)
|
||||
{
|
||||
int on = 1;
|
||||
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
|
||||
(void *)&on, sizeof(on)) < 0)
|
||||
throw Exception("error setting SO_REUSEPORT on socket");
|
||||
}
|
||||
#endif
|
||||
|
||||
// set SO_REUSEADDR for TCP
|
||||
inline void reuseaddr(const int fd)
|
||||
{
|
||||
int on = 1;
|
||||
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void *)&on, sizeof(on)) < 0)
|
||||
throw Exception("error setting SO_REUSEADDR on socket");
|
||||
}
|
||||
|
||||
// set TCP_NODELAY for TCP
|
||||
inline void tcp_nodelay(const int fd)
|
||||
{
|
||||
int state = 1;
|
||||
if (::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
(void *)&state, sizeof(state)) != 0)
|
||||
throw Exception("error setting TCP_NODELAY on socket");
|
||||
}
|
||||
|
||||
// set FD_CLOEXEC to prevent fd from being passed across execs
|
||||
inline void set_cloexec(const int fd)
|
||||
{
|
||||
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
|
||||
throw Exception("error setting FD_CLOEXEC on file-descriptor/socket");
|
||||
}
|
||||
|
||||
// set non-block mode on socket
|
||||
static inline void set_nonblock(const int fd)
|
||||
{
|
||||
if (::fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
||||
throw Exception("error setting socket to non-blocking mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 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/>.
|
||||
|
||||
// define stuff like ntohl, ntohs, htonl, htons, etc. in a platform-independent way
|
||||
|
||||
#ifndef OPENVPN_COMMON_SOCKTYPES_H
|
||||
#define OPENVPN_COMMON_SOCKTYPES_H
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#endif // OPENVPN_COMMON_SOCKTYPES_H
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General string-splitting methods. These methods along with lexical analyzer
|
||||
// classes (such as those defined in lex.hpp and OptionList::LexComment) can be
|
||||
// used as a basis for parsers.
|
||||
|
||||
#ifndef OPENVPN_COMMON_SPLIT_H
|
||||
#define OPENVPN_COMMON_SPLIT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/lex.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Split {
|
||||
enum {
|
||||
TRIM_LEADING_SPACES=(1<<0),
|
||||
TRIM_SPECIAL=(1<<1), // trims quotes (but respects their content)
|
||||
};
|
||||
|
||||
struct NullLimit
|
||||
{
|
||||
void add_term() {}
|
||||
};
|
||||
|
||||
// Split a string using a character (such as ',') as a separator.
|
||||
// Types:
|
||||
// V : string vector of return data
|
||||
// LEX : lexical analyzer class such as StandardLex
|
||||
// LIM : limit class such as OptionList::Limits
|
||||
// Args:
|
||||
// ret : return data -- a list of strings
|
||||
// input : input string to be split
|
||||
// split_by : separator
|
||||
// flags : TRIM_LEADING_SPACES, TRIM_SPECIAL
|
||||
// max_terms : the size of the returned string list will be, at most, this value + 1. Pass
|
||||
// ~0 to disable.
|
||||
// lim : an optional limits object such as OptionList::Limits
|
||||
template <typename V, typename LEX, typename LIM>
|
||||
inline void by_char_void(V& ret, const std::string& input, const char split_by, const unsigned int flags=0, const unsigned int max_terms=~0, LIM* lim=nullptr)
|
||||
{
|
||||
LEX lex;
|
||||
unsigned int nterms = 0;
|
||||
std::string term;
|
||||
for (std::string::const_iterator i = input.begin(); i != input.end(); ++i)
|
||||
{
|
||||
const char c = *i;
|
||||
lex.put(c);
|
||||
if (!lex.in_quote() && c == split_by && nterms < max_terms)
|
||||
{
|
||||
if (lim)
|
||||
lim->add_term();
|
||||
ret.push_back(std::move(term));
|
||||
++nterms;
|
||||
term = "";
|
||||
}
|
||||
else if ((!(flags & TRIM_SPECIAL) || lex.available())
|
||||
&& (!(flags & TRIM_LEADING_SPACES) || !term.empty() || !SpaceMatch::is_space(c)))
|
||||
term += c;
|
||||
}
|
||||
if (lim)
|
||||
lim->add_term();
|
||||
ret.push_back(std::move(term));
|
||||
}
|
||||
|
||||
// convenience method that returns data rather than modifying an in-place argument
|
||||
template <typename V, typename LEX, typename LIM>
|
||||
inline V by_char(const std::string& input, const char split_by, const unsigned int flags=0, const unsigned int max_terms=~0, LIM* lim=nullptr)
|
||||
{
|
||||
V ret;
|
||||
by_char_void<V, LEX, LIM>(ret, input, split_by, flags, max_terms, lim);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Split a string using spaces as a separator.
|
||||
// Types:
|
||||
// V : string vector of return data
|
||||
// LEX : lexical analyzer class such as StandardLex
|
||||
// SPACE : class that we use to differentiate between space and non-space chars
|
||||
// LIM : limit class such as OptionList::Limits
|
||||
// Args:
|
||||
// ret : return data -- a list of strings
|
||||
// input : input string to be split
|
||||
// lim : an optional limits object such as OptionList::Limits
|
||||
template <typename V, typename LEX, typename SPACE, typename LIM>
|
||||
inline void by_space_void(V& ret, const std::string& input, LIM* lim=nullptr)
|
||||
{
|
||||
LEX lex;
|
||||
|
||||
std::string term;
|
||||
bool defined = false;
|
||||
for (std::string::const_iterator i = input.begin(); i != input.end(); ++i)
|
||||
{
|
||||
const char c = *i;
|
||||
lex.put(c);
|
||||
if (lex.in_quote())
|
||||
defined = true;
|
||||
if (lex.available())
|
||||
{
|
||||
const char tc = lex.get();
|
||||
if (!SPACE::is_space(tc) || lex.in_quote())
|
||||
{
|
||||
defined = true;
|
||||
term += tc;
|
||||
}
|
||||
else if (defined)
|
||||
{
|
||||
if (lim)
|
||||
lim->add_term();
|
||||
ret.push_back(std::move(term));
|
||||
term = "";
|
||||
defined = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defined)
|
||||
{
|
||||
if (lim)
|
||||
lim->add_term();
|
||||
ret.push_back(std::move(term));
|
||||
}
|
||||
}
|
||||
|
||||
// convenience method that returns data rather than modifying an in-place argument
|
||||
template <typename V, typename LEX, typename SPACE, typename LIM>
|
||||
inline V by_space(const std::string& input, LIM* lim=nullptr)
|
||||
{
|
||||
V ret;
|
||||
by_space_void<V, LEX, SPACE, LIM>(ret, input, lim);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_SPLIT_H
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General purpose class to split a multi-line string into lines.
|
||||
|
||||
#ifndef OPENVPN_COMMON_SPLITLINES_H
|
||||
#define OPENVPN_COMMON_SPLITLINES_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
template <typename STRING>
|
||||
class SplitLinesType
|
||||
{
|
||||
public:
|
||||
// Note: string/buffer passed to constructor is not locally stored,
|
||||
// so it must remain in scope and not be modified during the lifetime
|
||||
// of the SplitLines object.
|
||||
SplitLinesType(const STRING& str, const size_t max_line_len_arg=0)
|
||||
: data((const char *)str.c_str()),
|
||||
size(str.length()),
|
||||
max_line_len(max_line_len_arg)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const bool trim=true)
|
||||
{
|
||||
line.clear();
|
||||
overflow = false;
|
||||
const size_t overflow_index = index + max_line_len;
|
||||
while (index < size)
|
||||
{
|
||||
if (max_line_len && index >= overflow_index)
|
||||
{
|
||||
overflow = true;
|
||||
return true;
|
||||
}
|
||||
const char c = data[index++];
|
||||
line += c;
|
||||
if (c == '\n' || index >= size)
|
||||
{
|
||||
if (trim)
|
||||
string::trim_crlf(line);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool line_overflow() const
|
||||
{
|
||||
return overflow;
|
||||
}
|
||||
|
||||
std::string& line_ref()
|
||||
{
|
||||
return line;
|
||||
}
|
||||
|
||||
const std::string& line_ref() const
|
||||
{
|
||||
return line;
|
||||
}
|
||||
|
||||
std::string line_move()
|
||||
{
|
||||
return std::move(line);
|
||||
}
|
||||
|
||||
enum Status {
|
||||
S_OKAY,
|
||||
S_EOF,
|
||||
S_ERROR
|
||||
};
|
||||
|
||||
Status next(std::string& ln, const bool trim=true)
|
||||
{
|
||||
const bool s = (*this)(trim);
|
||||
if (!s)
|
||||
return S_EOF;
|
||||
if (overflow)
|
||||
return S_ERROR;
|
||||
ln = std::move(line);
|
||||
return S_OKAY;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *data;
|
||||
size_t size;
|
||||
size_t max_line_len;
|
||||
size_t index = 0;
|
||||
std::string line;
|
||||
bool overflow = false;
|
||||
};
|
||||
|
||||
typedef SplitLinesType<std::string> SplitLines;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 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_COMMON_STAT_H
|
||||
#define OPENVPN_COMMON_STAT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdint> // for std::uint64_t
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Return true if file exists
|
||||
inline bool file_exists(const std::string& filename)
|
||||
{
|
||||
if (filename.empty())
|
||||
return false;
|
||||
struct stat buffer;
|
||||
return stat(filename.c_str(), &buffer) == 0;
|
||||
}
|
||||
|
||||
// Return file modification time (in seconds since unix epoch) or 0 on error
|
||||
inline time_t file_mod_time(const std::string& filename)
|
||||
{
|
||||
struct stat buffer;
|
||||
if (stat(filename.c_str(), &buffer) != 0)
|
||||
return 0;
|
||||
else
|
||||
return buffer.st_mtime;
|
||||
}
|
||||
|
||||
// Return file modification time (in nanoseconds since unix epoch) or 0 on error
|
||||
inline std::uint64_t file_mod_time_nanoseconds(const std::string& filename)
|
||||
{
|
||||
typedef std::uint64_t T;
|
||||
struct stat s;
|
||||
if (::stat(filename.c_str(), &s) == 0)
|
||||
return T(s.st_mtim.tv_sec) * T(1000000000) + T(s.st_mtim.tv_nsec);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return file modification time (in milliseconds since unix epoch) or 0 on error
|
||||
inline std::uint64_t file_mod_time_milliseconds(const std::string& filename)
|
||||
{
|
||||
return file_mod_time_nanoseconds(filename) / std::uint64_t(1000000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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 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_COMMON_STOP_H
|
||||
#define OPENVPN_COMMON_STOP_H
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
|
||||
namespace openvpn {
|
||||
class Stop
|
||||
{
|
||||
public:
|
||||
class Scope
|
||||
{
|
||||
friend Stop;
|
||||
|
||||
public:
|
||||
Scope(Stop* stop_arg, std::function<void()>&& method_arg)
|
||||
: stop(stop_arg),
|
||||
method(std::move(method_arg)),
|
||||
index(-1)
|
||||
{
|
||||
if (stop)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(stop->mutex);
|
||||
if (stop->stop_called)
|
||||
{
|
||||
// stop already called, call method immediately
|
||||
method();
|
||||
}
|
||||
else
|
||||
{
|
||||
index = stop->scopes.size();
|
||||
stop->scopes.push_back(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Scope()
|
||||
{
|
||||
if (stop)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(stop->mutex);
|
||||
if (index >= 0 && index < stop->scopes.size() && stop->scopes[index] == this)
|
||||
{
|
||||
stop->scopes[index] = nullptr;
|
||||
stop->prune();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Scope(const Scope&) = delete;
|
||||
Scope& operator=(const Scope&) = delete;
|
||||
|
||||
Stop *const stop;
|
||||
const std::function<void()> method;
|
||||
int index;
|
||||
};
|
||||
|
||||
Stop()
|
||||
{
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
stop_called = true;
|
||||
while (scopes.size())
|
||||
{
|
||||
Scope* scope = scopes.back();
|
||||
scopes.pop_back();
|
||||
if (scope)
|
||||
{
|
||||
scope->index = -1;
|
||||
scope->method();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Stop(const Stop&) = delete;
|
||||
Stop& operator=(const Stop&) = delete;
|
||||
|
||||
void prune()
|
||||
{
|
||||
while (scopes.size() && !scopes.back())
|
||||
scopes.pop_back();
|
||||
}
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
std::vector<Scope*> scopes;
|
||||
bool stop_called = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 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_COMMON_STRERROR_H
|
||||
#define OPENVPN_COMMON_STRERROR_H
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace openvpn {
|
||||
inline std::string strerror_str(const int errnum)
|
||||
{
|
||||
static const char unknown_err[] = "UNKNOWN_SYSTEM_ERROR";
|
||||
char buf[128];
|
||||
|
||||
#if defined(__GLIBC__) && (!defined(__USE_XOPEN2K) || defined(__USE_GNU))
|
||||
// GNU
|
||||
const char *errstr = ::strerror_r(errnum, buf, sizeof(buf));
|
||||
if (errstr)
|
||||
return std::string(errstr);
|
||||
#else
|
||||
// POSIX
|
||||
if (::strerror_r(errnum, buf, sizeof(buf)) == 0)
|
||||
return std::string(buf);
|
||||
#endif
|
||||
return std::string(unknown_err);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,652 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General purpose string-manipulation functions.
|
||||
|
||||
#ifndef OPENVPN_COMMON_STRING_H
|
||||
#define OPENVPN_COMMON_STRING_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace string {
|
||||
// case insensitive compare functions
|
||||
|
||||
inline int strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
return ::_stricmp(s1, s2);
|
||||
#else
|
||||
return ::strcasecmp(s1, s2);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int strcasecmp(const std::string& s1, const char *s2)
|
||||
{
|
||||
return strcasecmp(s1.c_str(), s2);
|
||||
}
|
||||
|
||||
inline int strcasecmp(const char *s1, const std::string& s2)
|
||||
{
|
||||
return strcasecmp(s1, s2.c_str());
|
||||
}
|
||||
|
||||
inline int strcasecmp(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
return strcasecmp(s1.c_str(), s2.c_str());
|
||||
}
|
||||
|
||||
// Like strncpy but makes sure dest is always null terminated
|
||||
inline void strncpynt (char *dest, const char *src, size_t maxlen)
|
||||
{
|
||||
strncpy (dest, src, maxlen);
|
||||
if (maxlen > 0)
|
||||
dest[maxlen - 1] = 0;
|
||||
}
|
||||
|
||||
// Copy string to dest, make sure dest is always null terminated,
|
||||
// and fill out trailing chars in dest with '\0' up to dest_size.
|
||||
inline void copy_fill (void *dest, const std::string& src, const size_t dest_size)
|
||||
{
|
||||
if (dest_size > 0)
|
||||
{
|
||||
const size_t ncopy = std::min(dest_size - 1, src.length());
|
||||
std::memcpy(dest, src.c_str(), ncopy);
|
||||
std::memset(static_cast<unsigned char *>(dest) + ncopy, 0, dest_size - ncopy);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_true(const std::string& str)
|
||||
{
|
||||
return str == "1" || !strcasecmp(str.c_str(), "true");
|
||||
}
|
||||
|
||||
inline bool starts_with(const std::string& str, const std::string& prefix)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
const size_t plen = prefix.length();
|
||||
if (plen <= len)
|
||||
return std::memcmp(str.c_str(), prefix.c_str(), plen) == 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool starts_with(const std::string& str, const char *prefix)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
const size_t plen = std::strlen(prefix);
|
||||
if (plen <= len)
|
||||
return std::memcmp(str.c_str(), prefix, plen) == 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ends_with(const std::string& str, const std::string& suffix)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
const size_t slen = suffix.length();
|
||||
if (slen <= len)
|
||||
return std::memcmp(str.c_str() + (len-slen), suffix.c_str(), slen) == 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool ends_with(const std::string& str, const char *suffix)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
const size_t slen = std::strlen(suffix);
|
||||
if (slen <= len)
|
||||
return std::memcmp(str.c_str() + (len-slen), suffix, slen) == 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if string ends with char c
|
||||
inline bool ends_with(const std::string& str, const char c)
|
||||
{
|
||||
return str.length() && str.back() == c;
|
||||
}
|
||||
|
||||
// return true if string ends with a newline
|
||||
inline bool ends_with_newline(const std::string& str)
|
||||
{
|
||||
return ends_with(str, '\n');
|
||||
}
|
||||
|
||||
// return true if string ends with a CR or LF
|
||||
inline bool ends_with_crlf(const std::string& str)
|
||||
{
|
||||
if (str.length())
|
||||
{
|
||||
const char c = str.back();
|
||||
return c == '\n' || c == '\r';
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepend leading characters (c) to str to obtain a minimum string length (min_len).
|
||||
// Useful for adding leading zeros to numeric values or formatting tables.
|
||||
inline std::string add_leading(const std::string& str, const size_t min_len, const char c)
|
||||
{
|
||||
if (min_len <= str.length())
|
||||
return str;
|
||||
size_t len = min_len - str.length();
|
||||
std::string ret;
|
||||
ret.reserve(min_len);
|
||||
while (len--)
|
||||
ret += c;
|
||||
ret += str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// make sure that string ends with char c, if not append it
|
||||
inline std::string add_trailing_copy(const std::string& str, const char c)
|
||||
{
|
||||
if (ends_with(str, c))
|
||||
return str;
|
||||
else
|
||||
return str + c;
|
||||
}
|
||||
|
||||
// make sure that string ends with char c, if not append it
|
||||
inline void add_trailing(std::string& str, const char c)
|
||||
{
|
||||
if (!ends_with(str, c))
|
||||
str += c;
|
||||
}
|
||||
|
||||
// make sure that string ends with CRLF, if not append it
|
||||
inline void add_trailing_crlf(std::string& str)
|
||||
{
|
||||
if (ends_with(str, "\r\n"))
|
||||
;
|
||||
else if (ends_with(str, '\r'))
|
||||
str += '\n';
|
||||
else if (ends_with(str, '\n'))
|
||||
{
|
||||
str.pop_back();
|
||||
str += "\r\n";
|
||||
}
|
||||
else
|
||||
str += "\r\n";
|
||||
}
|
||||
|
||||
// make sure that string ends with CRLF, if not append it
|
||||
inline std::string add_trailing_crlf_copy(std::string str)
|
||||
{
|
||||
add_trailing_crlf(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
// make sure that string ends with char c, if not append it (unless the string is empty)
|
||||
inline std::string add_trailing_unless_empty_copy(const std::string& str, const char c)
|
||||
{
|
||||
if (str.empty() || ends_with(str, c))
|
||||
return str;
|
||||
else
|
||||
return str + c;
|
||||
}
|
||||
|
||||
// remove trailing \r or \n chars
|
||||
inline void trim_crlf(std::string& str)
|
||||
{
|
||||
while (ends_with_crlf(str))
|
||||
str.pop_back();
|
||||
}
|
||||
|
||||
// remove trailing \r or \n chars
|
||||
inline std::string trim_crlf_copy(std::string str)
|
||||
{
|
||||
trim_crlf(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
// return true if str of size len contains an embedded null
|
||||
inline bool embedded_null(const char *str, size_t len)
|
||||
{
|
||||
while (len--)
|
||||
if (!*str++)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the length of a string, omitting trailing nulls
|
||||
inline size_t len_without_trailing_nulls(const char *str, size_t len)
|
||||
{
|
||||
while (len > 0 && str[len-1] == '\0')
|
||||
--len;
|
||||
return len;
|
||||
}
|
||||
|
||||
// return true if string contains at least one newline
|
||||
inline bool is_multiline(const std::string& str)
|
||||
{
|
||||
return str.find_first_of('\n') != std::string::npos;
|
||||
}
|
||||
|
||||
// Return string up to a delimiter (without the delimiter).
|
||||
// Returns the entire string if no delimiter is found.
|
||||
inline std::string to_delim(const std::string& str, const char delim)
|
||||
{
|
||||
const size_t pos = str.find_first_of(delim);
|
||||
if (pos != std::string::npos)
|
||||
return str.substr(0, pos);
|
||||
else
|
||||
return str;
|
||||
}
|
||||
|
||||
// return the first line (without newline) of a multi-line string
|
||||
inline std::string first_line(const std::string& str)
|
||||
{
|
||||
return to_delim(str, '\n');
|
||||
}
|
||||
|
||||
// Define a common interpretation of what constitutes a space character.
|
||||
// Return true if c is a space char.
|
||||
inline bool is_space(const char c)
|
||||
{
|
||||
return std::isspace(static_cast<unsigned char>(c)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_digit(const char c)
|
||||
{
|
||||
return std::isdigit(static_cast<unsigned char>(c)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_alpha(const char c)
|
||||
{
|
||||
return std::isalpha(static_cast<unsigned char>(c)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_alphanumeric(const char c)
|
||||
{
|
||||
return std::isalnum(static_cast<unsigned char>(c)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_printable(const char c)
|
||||
{
|
||||
return std::isprint(static_cast<unsigned char>(c)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_printable(const unsigned char c)
|
||||
{
|
||||
return std::isprint(c) != 0;
|
||||
}
|
||||
|
||||
inline bool is_ctrl(const char c)
|
||||
{
|
||||
return std::iscntrl(static_cast<unsigned char>(c)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_ctrl(const unsigned char c)
|
||||
{
|
||||
return std::iscntrl(c) != 0;
|
||||
}
|
||||
|
||||
// return true if string conforms to regex \w*
|
||||
inline bool is_word(const std::string& str)
|
||||
{
|
||||
for (auto &c : str)
|
||||
if (!(is_alphanumeric(c) || c == '_'))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// return true if all string characters are printable (or if string is empty)
|
||||
inline bool is_printable(const std::string& str)
|
||||
{
|
||||
for (auto &c : str)
|
||||
if (!is_printable(c))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// return true if str contains at least one non-space control char
|
||||
inline bool contains_non_space_ctrl(const std::string& str)
|
||||
{
|
||||
for (auto &c : str)
|
||||
if ((!is_space(c) && is_ctrl(c)) || c == 127)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if str contains at least one space char
|
||||
inline bool contains_space(const std::string& str)
|
||||
{
|
||||
for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
|
||||
if (is_space(*i))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove all spaces in string
|
||||
inline std::string remove_spaces(const std::string& str)
|
||||
{
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
|
||||
{
|
||||
char c = *i;
|
||||
if (!is_space(c))
|
||||
ret += c;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// replace all spaces in string with rep
|
||||
inline std::string replace_spaces(const std::string& str, const char rep)
|
||||
{
|
||||
std::string ret;
|
||||
for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
|
||||
{
|
||||
char c = *i;
|
||||
if (is_space(c))
|
||||
c = rep;
|
||||
ret += c;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// replace all spaces in string with rep, reducing instances of multiple
|
||||
// consecutive spaces to a single instance of rep and removing leading
|
||||
// and trailing spaces
|
||||
inline std::string reduce_spaces(const std::string& str, const char rep)
|
||||
{
|
||||
std::string ret;
|
||||
bool last_space = true;
|
||||
for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
|
||||
{
|
||||
char c = *i;
|
||||
const bool space = is_space(c);
|
||||
if (is_space(c))
|
||||
c = rep;
|
||||
if (!(space && last_space))
|
||||
ret += c;
|
||||
last_space = space;
|
||||
}
|
||||
if (last_space && !ret.empty())
|
||||
ret.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// generate a string with spaces
|
||||
inline std::string spaces(int n)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(n);
|
||||
while (n-- > 0)
|
||||
ret += ' ';
|
||||
return ret;
|
||||
}
|
||||
|
||||
// indent a multiline string
|
||||
inline std::string indent(const std::string& str, const int first, const int remaining)
|
||||
{
|
||||
std::string ret;
|
||||
int n_spaces = first;
|
||||
for (auto &c : str)
|
||||
{
|
||||
if (n_spaces)
|
||||
ret += spaces(n_spaces);
|
||||
n_spaces = 0;
|
||||
ret += c;
|
||||
if (c == '\n')
|
||||
n_spaces = remaining;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// replace instances of char 'from' in string with char 'to'
|
||||
inline std::string replace_copy(const std::string& str, const char from, const char to)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length());
|
||||
for (auto &c : str)
|
||||
ret.push_back(c == from ? to : c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return true if str is empty or contains only space chars
|
||||
inline bool is_empty(const std::string& str)
|
||||
{
|
||||
for (const auto& c : str)
|
||||
if (!is_space(c))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// return true if str is empty or contains only space chars
|
||||
inline bool is_empty(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
return true;
|
||||
char c;
|
||||
while ((c = *str++) != '\0')
|
||||
if (!is_space(c))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// convert \n to \r\n
|
||||
inline std::string unix2dos(const std::string& str, const bool force_eol=false)
|
||||
{
|
||||
std::string ret;
|
||||
bool last_char_was_cr = false;
|
||||
|
||||
ret.reserve(str.length() + str.length() / 8);
|
||||
for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
|
||||
{
|
||||
const char c = *i;
|
||||
if (c == '\n' && !last_char_was_cr)
|
||||
ret += '\r';
|
||||
ret += c;
|
||||
last_char_was_cr = (c == '\r');
|
||||
}
|
||||
if (force_eol)
|
||||
add_trailing_crlf(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Split a string on sep delimiter. The size of the
|
||||
// returned string vector will be at least 1 and at
|
||||
// most maxsplit + 1 (unless maxsplit is passed as -1).
|
||||
inline std::vector<std::string> split(const std::string& str,
|
||||
const char sep,
|
||||
const int maxsplit = -1)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
int nterms = 0;
|
||||
std::string term;
|
||||
|
||||
if (maxsplit >= 0)
|
||||
ret.reserve(maxsplit + 1);
|
||||
|
||||
for (const auto c : str)
|
||||
{
|
||||
if (c == sep && (maxsplit < 0 || nterms < maxsplit))
|
||||
{
|
||||
ret.push_back(std::move(term));
|
||||
++nterms;
|
||||
term.clear();
|
||||
}
|
||||
else
|
||||
term += c;
|
||||
}
|
||||
ret.push_back(std::move(term));
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string join(const std::vector<std::string>& strings,
|
||||
const std::string& delim,
|
||||
const bool tail=false)
|
||||
{
|
||||
std::string ret;
|
||||
bool first = true;
|
||||
for (const auto &s : strings)
|
||||
{
|
||||
if (!first)
|
||||
ret += delim;
|
||||
ret += s;
|
||||
first = false;
|
||||
}
|
||||
if (tail && !ret.empty())
|
||||
ret += delim;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::vector<std::string> from_argv(int argc, char *argv[], const bool skip_first)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
for (int i = (skip_first ? 1 : 0); i < argc; ++i)
|
||||
ret.emplace_back(argv[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string trim_left_copy(const std::string& str)
|
||||
{
|
||||
for (size_t i = 0; i < str.length(); ++i)
|
||||
{
|
||||
if (!is_space(str[i]))
|
||||
return str.substr(i);
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
inline std::string trim_copy(const std::string& str)
|
||||
{
|
||||
for (size_t i = 0; i < str.length(); ++i)
|
||||
{
|
||||
if (!is_space(str[i]))
|
||||
{
|
||||
size_t last_nonspace = i;
|
||||
for (size_t j = i + 1; j < str.length(); ++j)
|
||||
{
|
||||
if (!is_space(str[j]))
|
||||
last_nonspace = j;
|
||||
}
|
||||
return str.substr(i, last_nonspace - i + 1);
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
inline std::string to_upper_copy(const std::string& str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length()+1);
|
||||
for (const auto &c : str)
|
||||
ret.push_back(std::toupper(static_cast<unsigned char>(c)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string to_lower_copy(const std::string& str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length()+1);
|
||||
for (const auto &c : str)
|
||||
ret.push_back(std::tolower(static_cast<unsigned char>(c)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void trim(std::string& str)
|
||||
{
|
||||
str = trim_copy(str);
|
||||
}
|
||||
|
||||
inline void trim_left(std::string& str)
|
||||
{
|
||||
str = trim_left_copy(str);
|
||||
}
|
||||
|
||||
inline void to_lower(std::string& str)
|
||||
{
|
||||
str = to_lower_copy(str);
|
||||
}
|
||||
|
||||
inline void to_upper(std::string& str)
|
||||
{
|
||||
str = to_upper_copy(str);
|
||||
}
|
||||
|
||||
// Replace any subsequence of consecutive space chars containing
|
||||
// at least one newline with a single newline char. If string
|
||||
// is non-empty and doesn't include a trailing newline, add one.
|
||||
inline std::string remove_blanks(const std::string& str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length()+1);
|
||||
|
||||
std::string spaces;
|
||||
bool in_space = false;
|
||||
bool has_nl = false;
|
||||
|
||||
for (auto &c : str)
|
||||
{
|
||||
const bool s = is_space(c);
|
||||
reprocess:
|
||||
if (in_space)
|
||||
{
|
||||
if (s)
|
||||
{
|
||||
if (c == '\n')
|
||||
has_nl = true;
|
||||
spaces += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (has_nl)
|
||||
ret += '\n';
|
||||
else
|
||||
ret += spaces;
|
||||
in_space = false;
|
||||
has_nl = false;
|
||||
spaces.clear();
|
||||
goto reprocess;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s)
|
||||
{
|
||||
in_space = true;
|
||||
goto reprocess;
|
||||
}
|
||||
else
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
if (!ret.empty() && !ends_with_newline(ret))
|
||||
ret += '\n';
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace string
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_STRING_H
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 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_COMMON_STRINGIZE_H
|
||||
#define OPENVPN_COMMON_STRINGIZE_H
|
||||
|
||||
// OPENVPN_STRINGIZE(x) -- put double-quotes around x
|
||||
|
||||
#define OPENVPN_STRINGIZE(x) OPENVPN_STRINGIZE2(x)
|
||||
#define OPENVPN_STRINGIZE2(x) #x
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 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/>.
|
||||
|
||||
|
||||
// Sometimes you want to accept a <typename STRING> in a
|
||||
// method, without knowing whether it's a const char *,
|
||||
// const std::string&, or nullptr. These methods help with
|
||||
// basic type-independent string operations.
|
||||
|
||||
#ifndef OPENVPN_COMMON_STRINGTEMPL_H
|
||||
#define OPENVPN_COMMON_STRINGTEMPL_H
|
||||
|
||||
#include <string>
|
||||
#include <cstddef> // for std::nullptr_t
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
namespace StringTempl {
|
||||
|
||||
// empty
|
||||
|
||||
inline bool empty(std::nullptr_t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool empty(const char *str)
|
||||
{
|
||||
return !str || str[0] == '\0';
|
||||
}
|
||||
|
||||
inline bool empty(const std::string& str)
|
||||
{
|
||||
return str.empty();
|
||||
}
|
||||
|
||||
// to_string
|
||||
|
||||
inline std::string to_string(std::nullptr_t)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
inline std::string to_string(const char *str)
|
||||
{
|
||||
if (str)
|
||||
return std::string(str);
|
||||
else
|
||||
return to_string(nullptr);
|
||||
}
|
||||
|
||||
inline std::string to_string(std::string&& str)
|
||||
{
|
||||
return std::move(str);
|
||||
}
|
||||
|
||||
inline const std::string& to_string(const std::string& str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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-2019 OpenVPN 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/>.
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
namespace openvpn {
|
||||
namespace crypto {
|
||||
|
||||
// Compare strings in a way that is more resistant to timing attacks.
|
||||
// s1 should be the string provided by the user, while s2 is the
|
||||
// "secret" string that we are comparing s1 against.
|
||||
// Our goal is to prevent timing data from leaking info about the
|
||||
// length or content of s2.
|
||||
// https://nachtimwald.com/2017/04/02/constant-time-string-comparison-in-c/
|
||||
inline bool str_neq(const char *s1, const char *s2)
|
||||
{
|
||||
unsigned int neq = 0;
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
size_t k = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
neq |= s1[i] ^ s2[j];
|
||||
|
||||
if (s1[i] == '\0')
|
||||
break;
|
||||
i++;
|
||||
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (s2[j] != '\0')
|
||||
j++;
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (s2[j] == '\0')
|
||||
k++;
|
||||
}
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
return bool(neq);
|
||||
}
|
||||
|
||||
inline bool str_neq(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
return str_neq(s1.c_str(), s2.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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 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_COMMON_TEMPFILE_H
|
||||
#define OPENVPN_COMMON_TEMPFILE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <cstring> // for memcpy
|
||||
#include <unistd.h> // for write, unlink, lseek
|
||||
#include <sys/types.h> // for lseek
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/write.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
#include <openvpn/buffer/bufread.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class TempFile
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(tempfile_exception);
|
||||
|
||||
TempFile(const std::string& fn_template,
|
||||
const bool fn_delete)
|
||||
: fn(new char[fn_template.length()+1]),
|
||||
del(fn_delete)
|
||||
{
|
||||
std::memcpy(fn.get(), fn_template.c_str(), fn_template.length()+1);
|
||||
const size_t pos = fn_template.find("XXXXXX");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
const int suffixlen = fn_template.length() - pos - 6;
|
||||
if (suffixlen > 0)
|
||||
fd.reset(::mkstemps(fn.get(), suffixlen));
|
||||
else
|
||||
fd.reset(::mkstemp(fn.get()));
|
||||
if (!fd.defined())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tempfile_exception, "error creating temporary file from template: " << fn_template << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
else
|
||||
OPENVPN_THROW(tempfile_exception, "badly formed temporary file template: " << fn_template);
|
||||
}
|
||||
|
||||
~TempFile()
|
||||
{
|
||||
fd.close();
|
||||
delete_file();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
const off_t off = ::lseek(fd(), 0, SEEK_SET);
|
||||
if (off < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tempfile_exception, "seek error on temporary file: " << filename() << " : " << strerror_str(eno));
|
||||
}
|
||||
if (off)
|
||||
OPENVPN_THROW(tempfile_exception, "unexpected seek on temporary file: " << filename());
|
||||
}
|
||||
|
||||
void truncate()
|
||||
{
|
||||
reset();
|
||||
if (::ftruncate(fd(), 0) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tempfile_exception, "ftruncate error on temporary file: " << filename() << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
|
||||
void write(const std::string& content)
|
||||
{
|
||||
const ssize_t size = write_retry(fd(), content.c_str(), content.length());
|
||||
if (size < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tempfile_exception, "error writing to temporary file: " << filename() << " : " << strerror_str(eno));
|
||||
}
|
||||
else if (size != content.length())
|
||||
{
|
||||
OPENVPN_THROW(tempfile_exception, "incomplete write to temporary file: " << filename());
|
||||
}
|
||||
}
|
||||
|
||||
std::string read()
|
||||
{
|
||||
BufferList buflist = buf_read(fd(), filename());
|
||||
return buflist.to_string();
|
||||
}
|
||||
|
||||
std::string filename() const
|
||||
{
|
||||
if (fn)
|
||||
return fn.get();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void close_file()
|
||||
{
|
||||
if (!fd.close())
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tempfile_exception, "error closing temporary file: " << filename() << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
|
||||
void set_delete(const bool del_flag)
|
||||
{
|
||||
del = del_flag;
|
||||
}
|
||||
|
||||
void delete_file()
|
||||
{
|
||||
if (fn && del)
|
||||
{
|
||||
::unlink(fn.get());
|
||||
del = false;
|
||||
}
|
||||
}
|
||||
|
||||
ScopedFD fd;
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> fn;
|
||||
bool del;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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 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/>.
|
||||
|
||||
// Define openvpn::to_string() to work around the fact that
|
||||
// std::to_string() is missing on Android.
|
||||
// http://stackoverflow.com/questions/22774009/android-ndk-stdto-string-support
|
||||
|
||||
#ifndef OPENVPN_COMMON_TO_STRING_H
|
||||
#define OPENVPN_COMMON_TO_STRING_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Convert an arbitrary argument to a string.
|
||||
|
||||
#ifndef OPENVPN_PLATFORM_ANDROID
|
||||
// numeric types
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
|
||||
inline std::string to_string(T value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
// non-numeric types
|
||||
template <typename T
|
||||
#ifndef OPENVPN_PLATFORM_ANDROID
|
||||
, typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0
|
||||
#endif
|
||||
>
|
||||
inline std::string to_string(const T& value)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << value;
|
||||
return os.str();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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 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_COMMON_UMASK_H
|
||||
#define OPENVPN_COMMON_UMASK_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace openvpn {
|
||||
// Note: not thread safe, since umask() is
|
||||
// documented to modify the process-wide file
|
||||
// mode creation mask.
|
||||
class UMask
|
||||
{
|
||||
public:
|
||||
UMask(mode_t new_umask)
|
||||
{
|
||||
umask_save = ::umask(new_umask);
|
||||
}
|
||||
|
||||
~UMask()
|
||||
{
|
||||
::umask(umask_save);
|
||||
}
|
||||
|
||||
private:
|
||||
UMask(const UMask&) = delete;
|
||||
UMask& operator=(const UMask&) = delete;
|
||||
|
||||
mode_t umask_save;
|
||||
};
|
||||
|
||||
struct UMaskPrivate : public UMask
|
||||
{
|
||||
UMaskPrivate()
|
||||
: UMask(077)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct UMaskDaemon : public UMask
|
||||
{
|
||||
UMaskDaemon()
|
||||
: UMask(S_IWOTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,653 @@
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
#ifndef OPENVPN_COMMON_UNICODE_IMPL_H
|
||||
#define OPENVPN_COMMON_UNICODE_IMPL_H
|
||||
|
||||
namespace openvpn {
|
||||
namespace Unicode {
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Conversions between UTF32, UTF-16, and UTF-8. Header file.
|
||||
|
||||
Several funtions are included here, forming a complete set of
|
||||
conversions between the three formats. UTF-7 is not included
|
||||
here, but is handled in a separate source file.
|
||||
|
||||
Each of these routines takes pointers to input buffers and output
|
||||
buffers. The input buffers are const.
|
||||
|
||||
Each routine converts the text between *sourceStart and sourceEnd,
|
||||
putting the result into the buffer between *targetStart and
|
||||
targetEnd. Note: the end pointers are *after* the last item: e.g.
|
||||
*(sourceEnd - 1) is the last item.
|
||||
|
||||
The return result indicates whether the conversion was successful,
|
||||
and if not, whether the problem was in the source or target buffers.
|
||||
(Only the first encountered problem is indicated.)
|
||||
|
||||
After the conversion, *sourceStart and *targetStart are both
|
||||
updated to point to the end of last text successfully converted in
|
||||
the respective buffers.
|
||||
|
||||
Input parameters:
|
||||
sourceStart - pointer to a pointer to the source buffer.
|
||||
The contents of this are modified on return so that
|
||||
it points at the next thing to be converted.
|
||||
targetStart - similarly, pointer to pointer to the target buffer.
|
||||
sourceEnd, targetEnd - respectively pointers to the ends of the
|
||||
two buffers, for overflow checking only.
|
||||
|
||||
These conversion functions take a ConversionFlags argument. When this
|
||||
flag is set to strict, both irregular sequences and isolated surrogates
|
||||
will cause an error. When the flag is set to lenient, both irregular
|
||||
sequences and isolated surrogates are converted.
|
||||
|
||||
Whether the flag is strict or lenient, all illegal sequences will cause
|
||||
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
|
||||
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
|
||||
must check for illegal sequences.
|
||||
|
||||
When the flag is set to lenient, characters over 0x10FFFF are converted
|
||||
to the replacement character; otherwise (when the flag is set to strict)
|
||||
they constitute an error.
|
||||
|
||||
Output parameters:
|
||||
The value "sourceIllegal" is returned from some routines if the input
|
||||
sequence is malformed. When "sourceIllegal" is returned, the source
|
||||
value will point to the illegal value that caused the problem. E.g.,
|
||||
in UTF-8 when a sequence is malformed, it points to the start of the
|
||||
malformed sequence.
|
||||
|
||||
Author: Mark E. Davis, 1994.
|
||||
Rev History: Rick McGowan, fixes & updates May 2001.
|
||||
Fixes & updates, Sept 2001.
|
||||
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
The following 4 definitions are compiler-specific.
|
||||
The C standard does not guarantee that wchar_t has at least
|
||||
16 bits, so wchar_t is no less portable than unsigned short!
|
||||
All should be unsigned values to avoid sign extension during
|
||||
bit mask & shift operations.
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
typedef unsigned int UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
/* Some fundamental constants */
|
||||
const UTF32 UNI_REPLACEMENT_CHAR = (UTF32)0x0000FFFD;
|
||||
const UTF32 UNI_MAX_BMP = (UTF32)0x0000FFFF;
|
||||
const UTF32 UNI_MAX_UTF16 = (UTF32)0x0010FFFF;
|
||||
const UTF32 UNI_MAX_UTF32 = (UTF32)0x7FFFFFFF;
|
||||
const UTF32 UNI_MAX_LEGAL_UTF32 = (UTF32)0x0010FFFF;
|
||||
|
||||
typedef enum {
|
||||
conversionOK, /* conversion successful */
|
||||
sourceExhausted, /* partial character in source, but hit end */
|
||||
targetExhausted, /* insuff. room in target for conversion */
|
||||
sourceIllegal /* source sequence is illegal/malformed */
|
||||
} ConversionResult;
|
||||
|
||||
typedef enum {
|
||||
strictConversion = 0,
|
||||
lenientConversion
|
||||
} ConversionFlags;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
|
||||
Author: Mark E. Davis, 1994.
|
||||
Rev History: Rick McGowan, fixes & updates May 2001.
|
||||
Sept 2001: fixed const & error conditions per
|
||||
mods suggested by S. Parent & A. Lillich.
|
||||
June 2002: Tim Dodd added detection and handling of incomplete
|
||||
source sequences, enhanced error detection, added casts
|
||||
to eliminate compiler warnings.
|
||||
July 2003: slight mods to back out aggressive FFFE detection.
|
||||
Jan 2004: updated switches in from-UTF8 conversions.
|
||||
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
|
||||
|
||||
See the header file "ConvertUTF.h" for complete documentation.
|
||||
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
const UTF32 halfBase = 0x0010000UL;
|
||||
const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
const UTF32 UNI_SUR_HIGH_START = (UTF32)0xD800;
|
||||
const UTF32 UNI_SUR_HIGH_END = (UTF32)0xDBFF;
|
||||
const UTF32 UNI_SUR_LOW_START = (UTF32)0xDC00;
|
||||
const UTF32 UNI_SUR_LOW_END = (UTF32)0xDFFF;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
inline ConversionResult ConvertUTF32toUTF16 (
|
||||
const UTF32** sourceStart, const UTF32* sourceEnd,
|
||||
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF32* source = *sourceStart;
|
||||
UTF16* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch;
|
||||
if (target >= targetEnd) {
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch = *source++;
|
||||
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
|
||||
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
*target++ = (UTF16)ch; /* normal case */
|
||||
}
|
||||
} else if (ch > UNI_MAX_LEGAL_UTF32) {
|
||||
if (flags == strictConversion) {
|
||||
result = sourceIllegal;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
if (target + 1 >= targetEnd) {
|
||||
--source; /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
inline ConversionResult ConvertUTF16toUTF32 (
|
||||
const UTF16** sourceStart, const UTF16* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF16* source = *sourceStart;
|
||||
UTF32* target = *targetStart;
|
||||
UTF32 ch, ch2;
|
||||
while (source < sourceEnd) {
|
||||
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
|
||||
ch = *source++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd) {
|
||||
ch2 = *source;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
|
||||
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++source;
|
||||
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
} else { /* We don't have the 16 bits following the high surrogate. */
|
||||
--source; /* return to the high surrogate */
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
} else if (flags == strictConversion) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target >= targetEnd) {
|
||||
source = oldSource; /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
*target++ = ch;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
#ifdef CVTUTF_DEBUG
|
||||
if (result == sourceIllegal) {
|
||||
fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Index into the table below with the first byte of a UTF-8 sequence to
|
||||
* get the number of trailing bytes that are supposed to follow it.
|
||||
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
|
||||
* left as-is for anyone who may want to do such conversion, which was
|
||||
* allowed in earlier algorithms.
|
||||
*/
|
||||
const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/*
|
||||
* Magic values subtracted from a buffer value during UTF8 conversion.
|
||||
* This table contains as many values as there might be trailing bytes
|
||||
* in a UTF-8 sequence.
|
||||
*/
|
||||
const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
/*
|
||||
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
|
||||
* into the first byte, depending on how many bytes follow. There are
|
||||
* as many entries in this table as there are UTF-8 sequence types.
|
||||
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
|
||||
* for *legal* UTF-8 will be 4 or fewer bytes total.
|
||||
*/
|
||||
const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* The interface converts a whole buffer to avoid function-call overhead.
|
||||
* Constants have been gathered. Loops & conditionals have been removed as
|
||||
* much as possible for efficiency, in favor of drop-through switches.
|
||||
* (See "Note A" at the bottom of the file for equivalent code.)
|
||||
* If your compiler supports it, the "isLegalUTF8" call can be turned
|
||||
* into an inline function.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
inline ConversionResult ConvertUTF16toUTF8 (
|
||||
const UTF16** sourceStart, const UTF16* sourceEnd,
|
||||
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF16* source = *sourceStart;
|
||||
UTF8* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch;
|
||||
unsigned short bytesToWrite = 0;
|
||||
const UTF32 byteMask = 0xBF;
|
||||
const UTF32 byteMark = 0x80;
|
||||
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
|
||||
ch = *source++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd) {
|
||||
UTF32 ch2 = *source;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
|
||||
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++source;
|
||||
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
} else { /* We don't have the 16 bits following the high surrogate. */
|
||||
--source; /* return to the high surrogate */
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
} else if (flags == strictConversion) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Figure out how many bytes the result will require */
|
||||
if (ch < (UTF32)0x80) { bytesToWrite = 1;
|
||||
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
|
||||
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
|
||||
} else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
|
||||
} else { bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
target += bytesToWrite;
|
||||
if (target > targetEnd) {
|
||||
source = oldSource; /* Back up source pointer! */
|
||||
target -= bytesToWrite; result = targetExhausted; break;
|
||||
}
|
||||
switch (bytesToWrite) { /* note: everything falls through. */
|
||||
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
|
||||
* This must be called with the length pre-determined by the first byte.
|
||||
* If not calling this from ConvertUTF8to*, then the length can be set by:
|
||||
* length = trailingBytesForUTF8[*source]+1;
|
||||
* and the sequence is illegal right away if there aren't that many bytes
|
||||
* available.
|
||||
* If presented with a length > 4, this returns false. The Unicode
|
||||
* definition of UTF-8 goes up to 4-byte sequences.
|
||||
*/
|
||||
|
||||
inline bool isLegalUTF8(const UTF8 *source, int length) {
|
||||
UTF8 a;
|
||||
const UTF8 *srcptr = source+length;
|
||||
switch (length) {
|
||||
default: return false;
|
||||
/* Everything else falls through when "true"... */
|
||||
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
|
||||
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
|
||||
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
|
||||
|
||||
switch (*source) {
|
||||
/* no fall-through in this inner switch */
|
||||
case 0xE0: if (a < 0xA0) return false; break;
|
||||
case 0xED: if (a > 0x9F) return false; break;
|
||||
case 0xF0: if (a < 0x90) return false; break;
|
||||
case 0xF4: if (a > 0x8F) return false; break;
|
||||
default: if (a < 0x80) return false;
|
||||
}
|
||||
|
||||
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
|
||||
}
|
||||
if (*source > 0xF4) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Exported function to return whether a UTF-8 sequence is legal or not.
|
||||
* This is not used here; it's just exported.
|
||||
*/
|
||||
inline bool isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
|
||||
int length = trailingBytesForUTF8[*source]+1;
|
||||
if (source+length > sourceEnd) {
|
||||
return false;
|
||||
}
|
||||
return isLegalUTF8(source, length);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
inline ConversionResult ConvertUTF8toUTF16 (
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF8* source = *sourceStart;
|
||||
UTF16* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (source + extraBytesToRead >= sourceEnd) {
|
||||
result = sourceExhausted; break;
|
||||
}
|
||||
/* Do this check whether lenient or strict */
|
||||
if (! isLegalUTF8(source, extraBytesToRead+1)) {
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead) {
|
||||
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
||||
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
||||
case 3: ch += *source++; ch <<= 6;
|
||||
case 2: ch += *source++; ch <<= 6;
|
||||
case 1: ch += *source++; ch <<= 6;
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (target >= targetEnd) {
|
||||
source -= (extraBytesToRead+1); /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
source -= (extraBytesToRead+1); /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
*target++ = (UTF16)ch; /* normal case */
|
||||
}
|
||||
} else if (ch > UNI_MAX_UTF16) {
|
||||
if (flags == strictConversion) {
|
||||
result = sourceIllegal;
|
||||
source -= (extraBytesToRead+1); /* return to the start */
|
||||
break; /* Bail out; shouldn't continue */
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
if (target + 1 >= targetEnd) {
|
||||
source -= (extraBytesToRead+1); /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
inline ConversionResult ConvertUTF32toUTF8 (
|
||||
const UTF32** sourceStart, const UTF32* sourceEnd,
|
||||
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF32* source = *sourceStart;
|
||||
UTF8* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch;
|
||||
unsigned short bytesToWrite = 0;
|
||||
const UTF32 byteMask = 0xBF;
|
||||
const UTF32 byteMark = 0x80;
|
||||
ch = *source++;
|
||||
if (flags == strictConversion ) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Figure out how many bytes the result will require. Turn any
|
||||
* illegally large UTF32 things (> Plane 17) into replacement chars.
|
||||
*/
|
||||
if (ch < (UTF32)0x80) { bytesToWrite = 1;
|
||||
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
|
||||
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
|
||||
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
|
||||
} else { bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
result = sourceIllegal;
|
||||
}
|
||||
|
||||
target += bytesToWrite;
|
||||
if (target > targetEnd) {
|
||||
--source; /* Back up source pointer! */
|
||||
target -= bytesToWrite; result = targetExhausted; break;
|
||||
}
|
||||
switch (bytesToWrite) { /* note: everything falls through. */
|
||||
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
inline ConversionResult ConvertUTF8toUTF32 (
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF8* source = *sourceStart;
|
||||
UTF32* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (source + extraBytesToRead >= sourceEnd) {
|
||||
result = sourceExhausted; break;
|
||||
}
|
||||
/* Do this check whether lenient or strict */
|
||||
if (! isLegalUTF8(source, extraBytesToRead+1)) {
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead) {
|
||||
case 5: ch += *source++; ch <<= 6;
|
||||
case 4: ch += *source++; ch <<= 6;
|
||||
case 3: ch += *source++; ch <<= 6;
|
||||
case 2: ch += *source++; ch <<= 6;
|
||||
case 1: ch += *source++; ch <<= 6;
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (target >= targetEnd) {
|
||||
source -= (extraBytesToRead+1); /* Back up the source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
if (ch <= UNI_MAX_LEGAL_UTF32) {
|
||||
/*
|
||||
* UTF-16 surrogate values are illegal in UTF-32, and anything
|
||||
* over Plane 17 (> 0x10FFFF) is illegal.
|
||||
*/
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
source -= (extraBytesToRead+1); /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
*target++ = ch;
|
||||
}
|
||||
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
|
||||
result = sourceIllegal;
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Note A.
|
||||
The fall-through switches in UTF-8 reading code save a
|
||||
temp variable, some decrements & conditionals. The switches
|
||||
are equivalent to the following loop:
|
||||
{
|
||||
int tmpBytesToRead = extraBytesToRead+1;
|
||||
do {
|
||||
ch += *source++;
|
||||
--tmpBytesToRead;
|
||||
if (tmpBytesToRead) ch <<= 6;
|
||||
} while (tmpBytesToRead > 0);
|
||||
}
|
||||
In UTF-8 writing code, the switches on "bytesToWrite" are
|
||||
similarly unrolled loops.
|
||||
|
||||
--------------------------------------------------------------------- */
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,301 @@
|
||||
// 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 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/>.
|
||||
|
||||
// General-purpose function for dealing with unicode.
|
||||
|
||||
#ifndef OPENVPN_COMMON_UNICODE_H
|
||||
#define OPENVPN_COMMON_UNICODE_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring> // for std::memcpy
|
||||
#include <algorithm> // for std::min
|
||||
#include <memory>
|
||||
#include <cctype>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/unicode-impl.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Unicode {
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(unicode_src_overflow);
|
||||
OPENVPN_SIMPLE_EXCEPTION(unicode_dest_overflow);
|
||||
OPENVPN_SIMPLE_EXCEPTION(unicode_malformed);
|
||||
|
||||
// Return true if the given buffer is a valid UTF-8 string.
|
||||
// Extra constraints:
|
||||
enum {
|
||||
UTF8_NO_CTRL = (1<<30), // no control chars allowed
|
||||
UTF8_NO_SPACE = (1<<31), // no space chars allowed
|
||||
};
|
||||
inline bool is_valid_utf8_uchar_buf(const unsigned char *source,
|
||||
size_t size,
|
||||
const size_t max_len_flags=0) // OR max length (or 0 to disable) with UTF8_x flags above
|
||||
{
|
||||
const size_t max_len = max_len_flags & ((size_t)UTF8_NO_CTRL-1); // NOTE -- use smallest flag value here
|
||||
size_t unicode_len = 0;
|
||||
while (size)
|
||||
{
|
||||
const unsigned char c = *source;
|
||||
if (c == '\0')
|
||||
return false;
|
||||
const int length = trailingBytesForUTF8[c]+1;
|
||||
if ((size_t)length > size)
|
||||
return false;
|
||||
if (!isLegalUTF8(source, length))
|
||||
return false;
|
||||
if (length == 1)
|
||||
{
|
||||
if ((max_len_flags & UTF8_NO_CTRL) && std::iscntrl(c))
|
||||
return false;
|
||||
if ((max_len_flags & UTF8_NO_SPACE) && std::isspace(c))
|
||||
return false;
|
||||
}
|
||||
|
||||
source += length;
|
||||
size -= length;
|
||||
++unicode_len;
|
||||
if (max_len && unicode_len > max_len)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename STRING>
|
||||
inline bool is_valid_utf8(const STRING& str, const size_t max_len_flags=0)
|
||||
{
|
||||
return is_valid_utf8_uchar_buf((const unsigned char *)str.c_str(), str.length(), max_len_flags);
|
||||
}
|
||||
|
||||
// Return the byte position in the string that corresponds with
|
||||
// the given character index. Return values:
|
||||
enum {
|
||||
UTF8_GOOD=0, // succeeded, result in index
|
||||
UTF8_BAD, // failed, string is not legal UTF8
|
||||
UTF8_RANGE, // failed, index is beyond end of string
|
||||
};
|
||||
template <typename STRING>
|
||||
inline int utf8_index(STRING& str, size_t& index)
|
||||
{
|
||||
const size_t size = str.length();
|
||||
size_t upos = 0;
|
||||
size_t pos = 0;
|
||||
while (pos < size)
|
||||
{
|
||||
const int len = trailingBytesForUTF8[(unsigned char)str[pos]]+1;
|
||||
if (pos + len > size || !isLegalUTF8((const unsigned char *)&str[pos], len))
|
||||
return UTF8_BAD;
|
||||
if (upos >= index)
|
||||
{
|
||||
index = pos;
|
||||
return UTF8_GOOD;
|
||||
}
|
||||
pos += len;
|
||||
++upos;
|
||||
}
|
||||
return UTF8_RANGE;
|
||||
}
|
||||
|
||||
// Truncate a UTF8 string if its length exceeds max_len
|
||||
template <typename STRING>
|
||||
inline void utf8_truncate(STRING& str, size_t max_len)
|
||||
{
|
||||
const int status = utf8_index(str, max_len);
|
||||
if (status == UTF8_GOOD || status == UTF8_BAD)
|
||||
str = str.substr(0, max_len);
|
||||
}
|
||||
|
||||
// Return a printable UTF-8 string, where bad UTF-8 chars and
|
||||
// control chars are mapped to '?'.
|
||||
// If max_len_flags > 0, print a maximum of max_len_flags chars.
|
||||
// If UTF8_PASS_FMT flag is set in max_len_flags, pass through \r\n\t
|
||||
enum {
|
||||
UTF8_PASS_FMT=(1<<31),
|
||||
UTF8_FILTER=(1<<30),
|
||||
};
|
||||
template <typename STRING>
|
||||
inline STRING utf8_printable(const STRING& str, size_t max_len_flags)
|
||||
{
|
||||
STRING ret;
|
||||
const size_t size = str.length();
|
||||
const size_t max_len = max_len_flags & ((size_t)UTF8_FILTER-1); // NOTE -- use smallest flag value here
|
||||
size_t upos = 0;
|
||||
size_t pos = 0;
|
||||
ret.reserve(std::min(str.length(), max_len) + 3); // add 3 for "..."
|
||||
while (pos < size)
|
||||
{
|
||||
if (!max_len || upos < max_len)
|
||||
{
|
||||
unsigned char c = str[pos];
|
||||
int len = trailingBytesForUTF8[c]+1;
|
||||
if (pos + len <= size
|
||||
&& c >= 0x20 && c != 0x7F
|
||||
&& isLegalUTF8((const unsigned char *)&str[pos], len))
|
||||
{
|
||||
// non-control, legal UTF-8
|
||||
ret.append(str, pos, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// control char or bad UTF-8 char
|
||||
if (c == '\r' || c == '\n' || c == '\t')
|
||||
{
|
||||
if (!(max_len_flags & UTF8_PASS_FMT))
|
||||
c = ' ';
|
||||
}
|
||||
else if (max_len_flags & UTF8_FILTER)
|
||||
c = 0;
|
||||
else
|
||||
c = '?';
|
||||
if (c)
|
||||
ret += c;
|
||||
len = 1;
|
||||
}
|
||||
pos += len;
|
||||
++upos;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.append("...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename STRING>
|
||||
inline size_t utf8_length(const STRING& str)
|
||||
{
|
||||
const size_t size = str.length();
|
||||
size_t upos = 0;
|
||||
size_t pos = 0;
|
||||
while (pos < size)
|
||||
{
|
||||
int len = std::min((int)trailingBytesForUTF8[(unsigned char)str[pos]]+1,
|
||||
(int)size);
|
||||
if (!isLegalUTF8((const unsigned char *)&str[pos], len))
|
||||
len = 1;
|
||||
pos += len;
|
||||
++upos;
|
||||
}
|
||||
return upos;
|
||||
}
|
||||
|
||||
inline void conversion_result_throw(const ConversionResult res)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case conversionOK:
|
||||
return;
|
||||
case sourceExhausted:
|
||||
throw unicode_src_overflow();
|
||||
case targetExhausted:
|
||||
throw unicode_dest_overflow();
|
||||
case sourceIllegal:
|
||||
throw unicode_malformed();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a UTF-8 string to UTF-16 little endian (no null termination in return)
|
||||
template <typename STRING>
|
||||
inline BufferPtr string_to_utf16(const STRING& str)
|
||||
{
|
||||
std::unique_ptr<UTF16[]> utf16_dest(new UTF16[str.length()]);
|
||||
const UTF8 *src = (UTF8 *)str.c_str();
|
||||
UTF16 *dest = utf16_dest.get();
|
||||
const ConversionResult res = ConvertUTF8toUTF16(&src, src + str.length(),
|
||||
&dest, dest + str.length(),
|
||||
lenientConversion);
|
||||
conversion_result_throw(res);
|
||||
BufferPtr ret(new BufferAllocated((dest - utf16_dest.get()) * 2, BufferAllocated::ARRAY));
|
||||
UTF8 *d = ret->data();
|
||||
for (const UTF16 *s = utf16_dest.get(); s < dest; ++s)
|
||||
{
|
||||
*d++ = *s & 0xFF;
|
||||
*d++ = (*s >> 8) & 0xFF;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class UTF8Iterator
|
||||
{
|
||||
public:
|
||||
struct Char
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned char data[4];
|
||||
bool valid;
|
||||
|
||||
const bool is_valid() const
|
||||
{
|
||||
return valid && len >= 1 && len <= sizeof(data);
|
||||
}
|
||||
|
||||
std::string str(const char *malformed)
|
||||
{
|
||||
if (is_valid())
|
||||
return std::string((char *)data, len);
|
||||
else
|
||||
return malformed;
|
||||
}
|
||||
};
|
||||
|
||||
UTF8Iterator(const std::string& str_arg)
|
||||
: str((unsigned char *)str_arg.c_str()),
|
||||
size(str_arg.length())
|
||||
{
|
||||
}
|
||||
|
||||
bool get(Char &c)
|
||||
{
|
||||
if (size)
|
||||
{
|
||||
unsigned int len = std::min((unsigned int)trailingBytesForUTF8[*str]+1,
|
||||
(unsigned int)size);
|
||||
if (isLegalUTF8(str, len))
|
||||
{
|
||||
c.valid = true;
|
||||
c.len = std::min(len, (unsigned int)sizeof(c.data));
|
||||
std::memcpy(c.data, str, c.len);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.valid = false;
|
||||
c.len = 1;
|
||||
}
|
||||
str += c.len;
|
||||
size -= c.len;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const unsigned char *str;
|
||||
unsigned int size;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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 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_COMMON_UNIQUEPTR_H
|
||||
#define OPENVPN_COMMON_UNIQUEPTR_H
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace openvpn {
|
||||
template<typename T>
|
||||
using unique_ptr_del = std::unique_ptr<T, std::function<void(T*)>>;
|
||||
}
|
||||
|
||||
#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 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/>.
|
||||
|
||||
// General purpose class for scope accounting.
|
||||
|
||||
#ifndef OPENVPN_COMMON_USECOUNT_H
|
||||
#define OPENVPN_COMMON_USECOUNT_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class UseCount
|
||||
{
|
||||
public:
|
||||
UseCount(int& count)
|
||||
: count_(count)
|
||||
{
|
||||
++count_;
|
||||
}
|
||||
|
||||
~UseCount()
|
||||
{
|
||||
--count_;
|
||||
}
|
||||
|
||||
private:
|
||||
int& count_;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMMON_USECOUNT_H
|
||||
@@ -0,0 +1,195 @@
|
||||
// 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 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_COMMON_USERGROUP_H
|
||||
#define OPENVPN_COMMON_USERGROUP_H
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_LINUX
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/strerror.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// NOTE: -- SetUserGroup object does not own passwd and group
|
||||
// objects, therefore *pw and *gr can change under us.
|
||||
class SetUserGroup
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(user_group_err);
|
||||
|
||||
SetUserGroup(const std::string& user, const std::string& group, const bool strict)
|
||||
: SetUserGroup(user.empty() ? nullptr : user.c_str(),
|
||||
group.empty() ? nullptr : group.c_str(),
|
||||
strict)
|
||||
{
|
||||
}
|
||||
|
||||
SetUserGroup(const char *user, const char *group, const bool strict)
|
||||
: pw(nullptr),
|
||||
gr(nullptr)
|
||||
{
|
||||
if (user)
|
||||
{
|
||||
pw = ::getpwnam(user);
|
||||
if (!pw && strict)
|
||||
OPENVPN_THROW(user_group_err, "user lookup failed for '" << user << '\'');
|
||||
user_name = user;
|
||||
}
|
||||
if (group)
|
||||
{
|
||||
gr = ::getgrnam(group);
|
||||
if (!gr && strict)
|
||||
OPENVPN_THROW(user_group_err, "group lookup failed for '" << group << '\'');
|
||||
group_name = group;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& user() const
|
||||
{
|
||||
return user_name;
|
||||
}
|
||||
|
||||
const std::string& group() const
|
||||
{
|
||||
return group_name;
|
||||
}
|
||||
|
||||
void activate() const
|
||||
{
|
||||
if (gr)
|
||||
{
|
||||
if (::setgid(gr->gr_gid))
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(user_group_err, "setgid failed for group '" << group_name << "': " << strerror_str(eno));
|
||||
}
|
||||
gid_t gr_list[1];
|
||||
gr_list[0] = gr->gr_gid;
|
||||
if (::setgroups(1, gr_list))
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(user_group_err, "setgroups failed for group '" << group_name << "': " << strerror_str(eno));
|
||||
}
|
||||
OPENVPN_LOG("GID set to '" << group_name << '\'');
|
||||
}
|
||||
if (pw)
|
||||
{
|
||||
if (::setuid(pw->pw_uid))
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(user_group_err, "setuid failed for user '" << user_name << "': " << strerror_str(eno));
|
||||
}
|
||||
OPENVPN_LOG("UID set to '" << user_name << '\'');
|
||||
}
|
||||
#ifdef OPENVPN_PLATFORM_LINUX
|
||||
// retain core dumpability after setgid/setuid
|
||||
if (gr || pw)
|
||||
::prctl(PR_SET_DUMPABLE, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void chown(const std::string& fn) const
|
||||
{
|
||||
if (pw && gr)
|
||||
{
|
||||
const int status = ::chown(fn.c_str(), uid(), gid());
|
||||
if (status < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(user_group_err, "chown " << user_name << '.' << group_name << ' ' << fn << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void chown(const int fd, const std::string& title) const
|
||||
{
|
||||
if (pw && gr)
|
||||
{
|
||||
const int status = ::fchown(fd, uid(), gid());
|
||||
if (status < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(user_group_err, "chown " << user_name << '.' << group_name << ' ' << title << " : " << strerror_str(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
pw = nullptr;
|
||||
gr = nullptr;
|
||||
}
|
||||
|
||||
uid_t uid() const
|
||||
{
|
||||
if (pw)
|
||||
return pw->pw_uid;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
gid_t gid() const
|
||||
{
|
||||
if (gr)
|
||||
return gr->gr_gid;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool uid_defined() const
|
||||
{
|
||||
return bool(pw);
|
||||
}
|
||||
|
||||
bool gid_defined() const
|
||||
{
|
||||
return bool(gr);
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return uid_defined() && gid_defined();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string user_name;
|
||||
std::string group_name;
|
||||
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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 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_COMMON_USERPASS_H
|
||||
#define OPENVPN_COMMON_USERPASS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace UserPass {
|
||||
|
||||
OPENVPN_EXCEPTION(creds_error);
|
||||
|
||||
enum Flags {
|
||||
OPT_REQUIRED = (1<<0), // option must be present
|
||||
OPT_OPTIONAL = (1<<1), // if option is not present, USERNAME_REQUIRED and PASSWORD_REQUIRED are ignored
|
||||
USERNAME_REQUIRED = (1<<2), // username must be present
|
||||
PASSWORD_REQUIRED = (1<<3), // password must be present
|
||||
TRY_FILE = (1<<4), // option argument might be a filename, try to load creds from it
|
||||
};
|
||||
|
||||
inline bool parse(const OptionList& options,
|
||||
const std::string& opt_name,
|
||||
const unsigned int flags,
|
||||
std::vector<std::string>* user_pass)
|
||||
{
|
||||
const Option* auth_user_pass = options.get_ptr(opt_name);
|
||||
if (!auth_user_pass)
|
||||
{
|
||||
if (flags & OPT_REQUIRED)
|
||||
throw creds_error(opt_name + " : credentials option missing");
|
||||
return false;
|
||||
}
|
||||
if (auth_user_pass->size() == 1 && !(flags & OPT_REQUIRED))
|
||||
return true;
|
||||
if (auth_user_pass->size() != 2)
|
||||
throw creds_error(opt_name + " : credentials option incorrectly specified");
|
||||
|
||||
std::string str = auth_user_pass->get(1, 1024 | Option::MULTILINE);
|
||||
if ((flags & TRY_FILE) && !string::is_multiline(str))
|
||||
str = read_text_utf8(str);
|
||||
SplitLines in(str, 1024);
|
||||
for (int i = 0; in(true) && i < 2; ++i)
|
||||
{
|
||||
if (user_pass)
|
||||
user_pass->push_back(in.line_move());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void parse(const OptionList& options,
|
||||
const std::string& opt_name,
|
||||
const unsigned int flags,
|
||||
std::string& user,
|
||||
std::string& pass)
|
||||
{
|
||||
std::vector<std::string> up;
|
||||
up.reserve(2);
|
||||
if (!parse(options, opt_name, flags, &up) && (flags & OPT_OPTIONAL))
|
||||
return;
|
||||
if (up.size() >= 1)
|
||||
{
|
||||
user = std::move(up[0]);
|
||||
if (up.size() >= 2)
|
||||
pass = std::move(up[1]);
|
||||
}
|
||||
if ((flags & USERNAME_REQUIRED) && string::is_empty(user))
|
||||
throw creds_error(opt_name + " : username empty");
|
||||
if ((flags & PASSWORD_REQUIRED) && string::is_empty(pass))
|
||||
throw creds_error(opt_name + " : password empty");
|
||||
}
|
||||
|
||||
inline void parse(const std::string& path,
|
||||
const unsigned int flags,
|
||||
std::string& user,
|
||||
std::string& pass)
|
||||
{
|
||||
user.clear();
|
||||
pass.clear();
|
||||
const std::string str = read_text_utf8(path);
|
||||
SplitLines in(str, 1024);
|
||||
if (in(true))
|
||||
{
|
||||
user = in.line_move();
|
||||
if (in(true))
|
||||
pass = in.line_move();
|
||||
}
|
||||
if ((flags & USERNAME_REQUIRED) && string::is_empty(user))
|
||||
throw creds_error(path + " : username empty");
|
||||
if ((flags & PASSWORD_REQUIRED) && string::is_empty(pass))
|
||||
throw creds_error(path + " : password empty");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 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/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(HAVE_VALGRIND)
|
||||
#include <valgrind/memcheck.h>
|
||||
#define OPENVPN_MAKE_MEM_DEFINED(addr, len) VALGRIND_MAKE_MEM_DEFINED(addr, len)
|
||||
#else
|
||||
#define OPENVPN_MAKE_MEM_DEFINED(addr, len)
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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-2018 OpenVPN 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/>.
|
||||
|
||||
// Current version of the OpenVPN core
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef OPENVPN_VERSION
|
||||
#define OPENVPN_VERSION "3.5.4"
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 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_COMMON_WAITBARRIER_H
|
||||
#define OPENVPN_COMMON_WAITBARRIER_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/pthreadcond.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
static constexpr unsigned int WAIT_BARRIER_TIMEOUT = 300;
|
||||
#else
|
||||
static constexpr unsigned int WAIT_BARRIER_TIMEOUT = 30;
|
||||
#endif
|
||||
|
||||
template <typename THREAD_COMMON>
|
||||
inline void event_loop_wait_barrier(THREAD_COMMON& tc,
|
||||
const unsigned int seconds=WAIT_BARRIER_TIMEOUT)
|
||||
{
|
||||
// barrier prior to event-loop entry
|
||||
switch (tc.event_loop_bar.wait(seconds))
|
||||
{
|
||||
case PThreadBarrier::SUCCESS:
|
||||
break;
|
||||
case PThreadBarrier::CHOSEN_ONE:
|
||||
tc.user_group.activate();
|
||||
tc.show_unused_options();
|
||||
tc.event_loop_bar.signal();
|
||||
break;
|
||||
case PThreadBarrier::TIMEOUT:
|
||||
throw Exception("event loop barrier timeout");
|
||||
case PThreadBarrier::ERROR:
|
||||
throw Exception("event loop barrier error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 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_COMMON_WRITE_H
|
||||
#define OPENVPN_COMMON_WRITE_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h> // defines std::abort()
|
||||
|
||||
namespace openvpn {
|
||||
// like posix write() but retry if full buffer is not written
|
||||
inline ssize_t write_retry(int fd, const void *buf, size_t count)
|
||||
{
|
||||
size_t total = 0;
|
||||
while (true)
|
||||
{
|
||||
const ssize_t status = ::write(fd, buf, count);
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (status > count) // should never happen
|
||||
std::abort();
|
||||
total += status;
|
||||
count -= status;
|
||||
if (!count)
|
||||
break;
|
||||
buf = static_cast<const unsigned char*>(buf) + status;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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 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_COMMON_WSTRING_H
|
||||
#define OPENVPN_COMMON_WSTRING_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <memory>
|
||||
|
||||
namespace openvpn {
|
||||
namespace wstring {
|
||||
|
||||
inline std::wstring from_utf8(const std::string& str)
|
||||
{
|
||||
typedef std::codecvt_utf8<wchar_t> cvt_type;
|
||||
std::wstring_convert<cvt_type, wchar_t> cvt;
|
||||
return cvt.from_bytes(str);
|
||||
}
|
||||
|
||||
inline std::string to_utf8(const std::wstring& wstr)
|
||||
{
|
||||
typedef std::codecvt_utf8<wchar_t> cvt_type;
|
||||
std::wstring_convert<cvt_type, wchar_t> cvt;
|
||||
return cvt.to_bytes(wstr);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<wchar_t[]> to_wchar_t(const std::wstring& wstr)
|
||||
{
|
||||
const size_t len = wstr.length();
|
||||
std::unique_ptr<wchar_t[]> ret(new wchar_t[len+1]);
|
||||
size_t i;
|
||||
for (i = 0; i < len; ++i)
|
||||
ret[i] = wstr[i];
|
||||
ret[i] = L'\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return value corresponds to the MULTI_SZ string format on Windows
|
||||
inline std::wstring pack_string_vector(const std::vector<std::string>& strvec)
|
||||
{
|
||||
std::wstring ret;
|
||||
for (auto &s : strvec)
|
||||
{
|
||||
ret += from_utf8(s);
|
||||
ret += L'\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user