Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions
+30
View File
@@ -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
+212
View File
@@ -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
+37
View File
@@ -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
+133
View File
@@ -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
+318
View File
@@ -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
+39
View File
@@ -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
+70
View File
@@ -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
+29
View File
@@ -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
+153
View File
@@ -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
+126
View File
@@ -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
+159
View File
@@ -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
+213
View File
@@ -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
+337
View File
@@ -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
+226
View File
@@ -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
+48
View File
@@ -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
+75
View File
@@ -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
+192
View File
@@ -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
+487
View File
@@ -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
+102
View File
@@ -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
+53
View File
@@ -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;
};
}
+159
View File
@@ -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
}
}
+70
View File
@@ -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));
}
}
}
+165
View File
@@ -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
+152
View File
@@ -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
+38
View File
@@ -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
+207
View File
@@ -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
+147
View File
@@ -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
+184
View File
@@ -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
+733
View File
@@ -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
+310
View File
@@ -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
+200
View File
@@ -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
+162
View File
@@ -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);
}
}
}
+42
View File
@@ -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
+50
View File
@@ -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
+157
View File
@@ -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
+70
View File
@@ -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
+119
View File
@@ -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
+652
View File
@@ -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
+68
View File
@@ -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
+301
View File
@@ -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
+50
View File
@@ -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