mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-07-08 00:00:02 +08:00
Squashed 'OpenVPN Adapter/Vendors/openvpn/' changes from 098fd412a..e6d68831a
e6d68831a deps: update mbedTLS to 2.7.0 59de63fa6 cli.cpp: added OPENVPN_REMOTE_OVERRIDE caf9cf6c1 RedirectPipe: added additional flags for flexibility 68595de4d ClientAPI::RemoteOverride: added error status 37d848ca2 Log lines from C++ exceptions should contain the text "exception" f05802cf9 Increase server validation of password size to 16KB to support bundling SAML messages. 52e4d4a5f Increase client validation of password/response size to 16KB to support bundling SAML messages. a0416ed92 [OVPN3-209] win: add dependencies checksum verification f6eadbc4d [OVPN3-206] Refactor Windows build system 7b30c2f12 [OVPN3-220] proto.hpp: send initial options set on rekeying 33dd2f29e mbedtls: backport fixes for CVE-2018-0487 0912a9b62 [OVPN3-213] build system: mbedtls timing tests 98fa55576 deps: update asio to 1.12.0 620531101 [OVPN3-215] asio: apply external patches f4a73bde5 [OVPN3-215] asio: rebase external patches on top of our current commit ID a61cac928 mbedtls: Patches from 2.7 to fix timing test failures c892f41fb win: tune dependencies build 8a394a37d [OVPN3-213] build system: mbedtls timing tests 0a3dd67da [OVPN3-190] tun linux: add to/from_json methods 44c6cdfdc [OVPN3-206] readme: update Windows build instructions 0edec4a09 [OVPN3-206] win: update directories in VS projects 3d6fd62cb mac build: improve unittest stability 758ae98c6 [OVPN3-209] win: add dependencies checksum verification a7642ee82 [OVPN3-205] win: apply mbedTLS patches ac94b6eb7 [OVPN3-206] Refactor Windows build system c5bc3859e mbedTLS: don't set endpoint twice in conf object 3d5dd9ee3 [OVPN3-199] mac build: do not overwrite DEP_DIR b713762ba mbedtls: Patches from 2.7 to fix timing test failures 37ab79fa6 tun linux: apply changes from 362acf0 6a7aee2c9 [OVPN3-190] tun: implement persistence 1d2ebb07f [OVPN3-190] tun: move tun creation to separate class 53e33d634 [OVPN3-190] tun: move content of tun to tuncli 85d3377c2 [OVPN3-190] tun: move tun setup methods to separate file 735b985eb i/o layer: wrap raw pointers embedded in closures 322ae24b5 OptionList: support variadic template parameter pack in constructors 8a012b454 lz4: added namespace and improved error handling 34998e94a zlib: removed verbose parameter 846ed217d OpenSSL: set SSL_MODE_RELEASE_BUFFERS to conserve memory by releasing unneeded buffers 32e3ea117 OptionList: added show_unused_options() method fe38233a8 Buffer: added typedefs for thread-safe refcounts b34b6271e compression: added compress_lz4() and decompress_lz4() 755e1a181 linux/core.hpp: added exclude_from_core() function a7f6fe64f ManClientInstance::Send: added userprop_local_update() virtual method 94526ac19 BufferAllocated: fixed regression in buffer copy 33c16812e [OVPN3-144] mbedTLS: fix support for 4096bit encrypted keys f249ab4bd [OVPN3-144] build-mbedtls: run make check before compiling 5040aef4c [OVPN3-144] build-mbedtls: apply patches using git-apply instead of patch 8a5e838ab [OVPN3-144] mbedTLS: fix incompatibility with PKI created by OpenSSL 1.1 e7badefd7 proto.hpp/tls-crypt: fix access to ACK IDs during packet validation 73fa974db proto.hpp: print buffer exception in case of packet access error 79ad5eded Estblishing a stable branch 1c5f20ab0 Hide the @ sign in logs if username is empty 01ee1f5a4 Added ClientAPI::Config::retryOnAuthFailed parameter 05880b136 Added ProfileParseLimits::MAX_SERVER_LIST_SIZE and raise limit to 4096 eedee4fa6 cli.cpp: allow -s server override to reference a friendly name 6e350e9f9 Linux tun setup: use LinuxGW46 to obtain gateway info 3e044c6c7 top-level .gitignore was missing a trailing newline a27355ac7 Use C++11 push_back(std::move(...))) for pushing objects onto vectors 8c3af2704 HostPort::split_host_port: support unix socket filename as an alternative kind of host 14b588c86 asio: added asio_resolver_results_to_string() fd6e8e9bf AsioPolySock: minor changes to remote_endpoint_str() 06f5e4d71 AsioBoundSocket::Socket: added to_string() method 8fd968532 RemoteList: minor cleanup in set_endpoint_range() f9fc2f54e BufferAllocated: improve movability 8cb8d52cd string: added first_line() method a26b1646b AsioPolySock: extend AltRouting support ef3a40c27 Listen::Item: added AltRouting mode 02e786bc9 write_binary_atomic: support ConstBuffer 6745799c9 fileunix: added read_binary_unix_fast() 5689c2d9c write_binary_unix(): added ConstBuffer variant 2b0e76453 enum_dir: refactor to allow enumeration via lambda 116a5bd5e bufstr: added const_buf_from_string() method f8ec81413 Buffer: added const_buffer_ref() variant accepting a const argument ae98aa8b6 AsioPolySock: support AltRouting 8f81479f1 AsioBoundSocket::Socket: support inheritance 9598918e9 ServerProto: added schedule_disconnect() method. 4516cf67b ServerProto: reset CoarseTime object when AsioTimer is canceled 0ffc76a0b Route: implement operator< so Route objects can be used as map/set keys. c4af9f68b event_loop_wait_barrier: raise default timeout to 30 seconds d7fe87540 appversion.hpp: rename VERSION -> BUILD_VERSION git-subtree-dir: OpenVPN Adapter/Vendors/openvpn git-subtree-split: e6d68831a71131b7d92fbea93d3b9cbe10ba2068
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdint> // for std::uint32_t
|
||||
#include <tuple>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
@@ -172,7 +173,12 @@ namespace openvpn {
|
||||
|
||||
bool operator==(const RouteType& other) const
|
||||
{
|
||||
return prefix_len == other.prefix_len && addr == other.addr;
|
||||
return std::tie(prefix_len, addr) == std::tie(other.prefix_len, other.addr);
|
||||
}
|
||||
|
||||
bool operator<(const RouteType& other) const
|
||||
{
|
||||
return std::tie(prefix_len, addr) < std::tie(other.prefix_len, other.addr);
|
||||
}
|
||||
|
||||
template <typename HASH>
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace openvpn {
|
||||
namespace CF {
|
||||
|
||||
// essentially a vector of void *, used as source for array and dictionary constructors
|
||||
typedef BufferAllocatedType<CFTypeRef> SrcList;
|
||||
typedef BufferAllocatedType<CFTypeRef, thread_unsafe_refcount> SrcList;
|
||||
|
||||
inline Array array(const SrcList& values)
|
||||
{
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle Exception: " << e.what());
|
||||
OPENVPN_LOG("MacLifeCycle exception: " << e.what());
|
||||
}
|
||||
|
||||
// cleanup
|
||||
@@ -306,7 +306,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacLifeCycle::action_timer_callback: " << e.what());
|
||||
OPENVPN_LOG("MacLifeCycle::action_timer_callback exception: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/common/extern.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace AsioBoundSocket {
|
||||
@@ -51,7 +52,31 @@ namespace openvpn {
|
||||
bind_local_port = port;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(64);
|
||||
if (bind_local_addr.defined())
|
||||
{
|
||||
ret += "local=[";
|
||||
ret += bind_local_addr.to_string();
|
||||
ret += "]:";
|
||||
ret += openvpn::to_string(bind_local_port);
|
||||
}
|
||||
try {
|
||||
const std::string re = openvpn::to_string(remote_endpoint());
|
||||
if (!ret.empty())
|
||||
ret += ' ';
|
||||
ret += "remote=";
|
||||
ret += re;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void async_connect_post_open(const protocol_type& protocol, openvpn_io::error_code& ec) override
|
||||
{
|
||||
if (bind_local_addr.defined())
|
||||
@@ -63,6 +88,7 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
IP::Addr bind_local_addr;
|
||||
unsigned short bind_local_port = 0;
|
||||
};
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
#include <openvpn/common/sockopt.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
#ifdef OPENVPN_POLYSOCK_SUPPORTS_BIND
|
||||
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
|
||||
#include <openvpn/asio/alt_routing.hpp>
|
||||
#elif defined(OPENVPN_POLYSOCK_SUPPORTS_BIND)
|
||||
#include <openvpn/asio/asioboundsock.hpp>
|
||||
#endif
|
||||
|
||||
@@ -67,6 +69,11 @@ namespace openvpn {
|
||||
virtual void tcp_nodelay() {}
|
||||
virtual void set_cloexec() {}
|
||||
|
||||
virtual int native_handle()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
virtual bool peercreds(SockOpt::Creds& cr)
|
||||
{
|
||||
@@ -74,6 +81,13 @@ namespace openvpn {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
|
||||
virtual bool alt_routing_enabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual bool is_open() const = 0;
|
||||
virtual bool is_local() const = 0;
|
||||
|
||||
@@ -112,10 +126,18 @@ namespace openvpn {
|
||||
socket.async_receive(buf, std::move(callback));
|
||||
}
|
||||
|
||||
#if !defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
|
||||
virtual std::string remote_endpoint_str() const override
|
||||
{
|
||||
return to_string(socket.remote_endpoint());
|
||||
try {
|
||||
return "TCP " + openvpn::to_string(socket.remote_endpoint());
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return "TCP";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual bool remote_ip_port(IP::Addr& addr, unsigned int& port) const override
|
||||
{
|
||||
@@ -124,7 +146,7 @@ namespace openvpn {
|
||||
port = socket.remote_endpoint().port();
|
||||
return true;
|
||||
}
|
||||
catch (std::exception&)
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -164,7 +186,24 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_POLYSOCK_SUPPORTS_BIND
|
||||
virtual int native_handle() override
|
||||
{
|
||||
return socket.native_handle();
|
||||
}
|
||||
|
||||
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
|
||||
virtual std::string remote_endpoint_str() const override
|
||||
{
|
||||
return "TCP ALT " + socket.to_string();
|
||||
}
|
||||
|
||||
virtual bool alt_routing_enabled() override
|
||||
{
|
||||
return socket.alt_routing_enabled();
|
||||
}
|
||||
|
||||
AltRouting::Socket socket;
|
||||
#elif defined(OPENVPN_POLYSOCK_SUPPORTS_BIND)
|
||||
AsioBoundSocket::Socket socket;
|
||||
#else
|
||||
openvpn_io::ip::tcp::socket socket;
|
||||
@@ -237,6 +276,11 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int native_handle() override
|
||||
{
|
||||
return socket.native_handle();
|
||||
}
|
||||
|
||||
openvpn_io::local::stream_protocol::socket socket;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <class EPRANGE>
|
||||
inline std::string asio_resolver_results_to_string(const EPRANGE& endpoint_range)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(64);
|
||||
bool first = true;
|
||||
for (const auto &i : endpoint_range)
|
||||
{
|
||||
if (!first)
|
||||
ret += ' ';
|
||||
ret += '[';
|
||||
ret += openvpn::to_string(i.endpoint().address());
|
||||
ret += "]:";
|
||||
ret += openvpn::to_string(i.endpoint().port());
|
||||
first = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,7 +57,8 @@ namespace openvpn {
|
||||
|
||||
bool is_valid_user_pass() const
|
||||
{
|
||||
return validate_auth_cred(username) && validate_auth_cred(password);
|
||||
return ValidateCreds::is_valid(ValidateCreds::USERNAME, username)
|
||||
&& ValidateCreds::is_valid(ValidateCreds::PASSWORD, password);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
|
||||
@@ -25,19 +25,38 @@
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// Authentication credential (username, password, or response) must
|
||||
// satisfy these constraints:
|
||||
//
|
||||
// 1. must be a valid UTF-8 string
|
||||
// 2. must not contain control or space characters
|
||||
// 3. length must be <= 256 unicode characters
|
||||
//
|
||||
// Note that we don't check that string is non-empty here,
|
||||
// callers should do this themselves if necessary.
|
||||
template <typename STRING>
|
||||
inline bool validate_auth_cred(const STRING& cred)
|
||||
{
|
||||
return Unicode::is_valid_utf8(cred, 256 | Unicode::UTF8_NO_CTRL | Unicode::UTF8_NO_SPACE);
|
||||
// Validate authentication credential.
|
||||
// Must be UTF-8.
|
||||
// Other checks on size and content below.
|
||||
// We don't check that the credential is non-empty.
|
||||
namespace ValidateCreds {
|
||||
|
||||
enum Type {
|
||||
USERNAME,
|
||||
PASSWORD,
|
||||
RESPONSE
|
||||
};
|
||||
|
||||
template <typename STRING>
|
||||
static bool is_valid(const Type type, const STRING& cred)
|
||||
{
|
||||
size_t max_len_flags;
|
||||
switch (type)
|
||||
{
|
||||
case USERNAME:
|
||||
// length <= 256 unicode chars, no control chars allowed
|
||||
max_len_flags = 256 | Unicode::UTF8_NO_CTRL;
|
||||
break;
|
||||
case PASSWORD:
|
||||
case RESPONSE:
|
||||
// length <= 16384 unicode chars
|
||||
max_len_flags = 16384;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return Unicode::is_valid_utf8(cred, max_len_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-17
@@ -53,7 +53,7 @@
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <type_traits> // for std::is_nothrow_move_constructible
|
||||
#include <type_traits> // for std::is_nothrow_move_constructible, std::remove_const
|
||||
|
||||
#ifndef OPENVPN_NO_IO
|
||||
#include <openvpn/io/io.hpp>
|
||||
@@ -136,11 +136,17 @@ namespace openvpn {
|
||||
Status status_;
|
||||
};
|
||||
|
||||
template <typename T, typename R>
|
||||
class BufferAllocatedType;
|
||||
|
||||
template <typename T>
|
||||
class BufferType {
|
||||
template <typename, typename> friend class BufferAllocatedType;
|
||||
|
||||
public:
|
||||
typedef T* type;
|
||||
typedef const T* const_type;
|
||||
typedef typename std::remove_const<T>::type NCT; // non-const type
|
||||
|
||||
BufferType()
|
||||
{
|
||||
@@ -469,14 +475,14 @@ namespace openvpn {
|
||||
prepend((const T*)data, size);
|
||||
}
|
||||
|
||||
void read(T* data, const size_t size)
|
||||
void read(NCT* data, const size_t size)
|
||||
{
|
||||
std::memcpy(data, read_alloc(size), size * sizeof(T));
|
||||
}
|
||||
|
||||
void read(void* data, const size_t size)
|
||||
{
|
||||
read((T*)data, size);
|
||||
read((NCT*)data, size);
|
||||
}
|
||||
|
||||
T* write_alloc(const size_t size)
|
||||
@@ -575,7 +581,7 @@ namespace openvpn {
|
||||
size_t capacity_; // maximum number of array objects of type T for which memory is allocated, starting at data_
|
||||
};
|
||||
|
||||
template <typename T, typename R = thread_unsafe_refcount>
|
||||
template <typename T, typename R>
|
||||
class BufferAllocatedType : public BufferType<T>, public RC<R>
|
||||
{
|
||||
using BufferType<T>::data_;
|
||||
@@ -583,6 +589,8 @@ namespace openvpn {
|
||||
using BufferType<T>::size_;
|
||||
using BufferType<T>::capacity_;
|
||||
|
||||
template <typename, typename> friend class BufferAllocatedType;
|
||||
|
||||
public:
|
||||
enum {
|
||||
CONSTRUCT_ZERO = (1<<0), // if enabled, constructors/init will zero allocated space
|
||||
@@ -636,19 +644,19 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OT>
|
||||
BufferAllocatedType(const BufferType<OT>& other, const unsigned int flags)
|
||||
template <typename T_>
|
||||
BufferAllocatedType(const BufferType<T_>& other, const unsigned int flags)
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(OT), "size inconsistency");
|
||||
offset_ = other.offset();
|
||||
size_ = other.size();
|
||||
capacity_ = other.capacity();
|
||||
static_assert(sizeof(T) == sizeof(T_), "size inconsistency");
|
||||
offset_ = other.offset_;
|
||||
size_ = other.size_;
|
||||
capacity_ = other.capacity_;
|
||||
flags_ = flags;
|
||||
if (capacity_)
|
||||
{
|
||||
data_ = new T[capacity_];
|
||||
if (size_)
|
||||
std::memcpy(data_ + offset_, other.c_data(), size_ * sizeof(T));
|
||||
std::memcpy(data_ + offset_, other.data_ + offset_, size_ * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,16 +732,17 @@ namespace openvpn {
|
||||
BufferType<T>::init_headroom(headroom);
|
||||
}
|
||||
|
||||
void move(BufferAllocatedType& other)
|
||||
template <typename T_, typename R_>
|
||||
void move(BufferAllocatedType<T_, R_>& other)
|
||||
{
|
||||
if (data_)
|
||||
delete_(data_, capacity_, flags_);
|
||||
move_(other);
|
||||
}
|
||||
|
||||
RCPtr<BufferAllocatedType<T>> move_to_ptr()
|
||||
RCPtr<BufferAllocatedType<T, R>> move_to_ptr()
|
||||
{
|
||||
RCPtr<BufferAllocatedType<T>> bp = new BufferAllocatedType<T>();
|
||||
RCPtr<BufferAllocatedType<T, R>> bp = new BufferAllocatedType<T, R>();
|
||||
bp->move(*this);
|
||||
return bp;
|
||||
}
|
||||
@@ -747,7 +756,8 @@ namespace openvpn {
|
||||
std::swap(flags_, other.flags_);
|
||||
}
|
||||
|
||||
BufferAllocatedType(BufferAllocatedType&& other) noexcept
|
||||
template <typename T_, typename R_>
|
||||
BufferAllocatedType(BufferAllocatedType<T_, R_>&& other) noexcept
|
||||
{
|
||||
move_(other);
|
||||
}
|
||||
@@ -812,7 +822,8 @@ namespace openvpn {
|
||||
capacity_ = newcap;
|
||||
}
|
||||
|
||||
void move_(BufferAllocatedType& other)
|
||||
template <typename T_, typename R_>
|
||||
void move_(BufferAllocatedType<T_, R_>& other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
offset_ = other.offset_;
|
||||
@@ -844,17 +855,30 @@ namespace openvpn {
|
||||
unsigned int flags_;
|
||||
};
|
||||
|
||||
// specializations of BufferType for unsigned char
|
||||
typedef BufferType<unsigned char> Buffer;
|
||||
typedef BufferType<const unsigned char> ConstBuffer;
|
||||
typedef BufferAllocatedType<unsigned char> BufferAllocated;
|
||||
typedef BufferAllocatedType<unsigned char, thread_unsafe_refcount> BufferAllocated;
|
||||
typedef RCPtr<BufferAllocated> BufferPtr;
|
||||
|
||||
// BufferAllocated with thread-safe refcount
|
||||
typedef BufferAllocatedType<unsigned char, thread_safe_refcount> BufferAllocatedTS;
|
||||
typedef RCPtr<BufferAllocatedTS> BufferPtrTS;
|
||||
|
||||
// cast BufferType<T> to BufferType<const T>
|
||||
|
||||
template <typename T>
|
||||
inline BufferType<const T>& const_buffer_ref(BufferType<T>& src)
|
||||
{
|
||||
return (BufferType<const T>&)src;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const BufferType<const T>& const_buffer_ref(const BufferType<T>& src)
|
||||
{
|
||||
return (const BufferType<const T>&)src;
|
||||
}
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_BUFFER_BUFFER_H
|
||||
|
||||
@@ -98,6 +98,13 @@ namespace openvpn {
|
||||
{
|
||||
buf.write((unsigned char *)str, std::strlen(str));
|
||||
}
|
||||
|
||||
// Note: ConstBuffer deep links to str, so returned ConstBuffer
|
||||
// is only defined while str is in scope.
|
||||
inline ConstBuffer const_buf_from_string(const std::string& str)
|
||||
{
|
||||
return ConstBuffer((const unsigned char *)str.c_str(), str.size(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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 <cstdint> // for std::uint32_t, uint64_t, etc.
|
||||
|
||||
#include <lz4.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/socktypes.hpp> // for ntohl/htonl
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace LZ4 {
|
||||
OPENVPN_EXCEPTION(lz4_error);
|
||||
|
||||
inline BufferPtr compress(const ConstBuffer& src,
|
||||
const size_t headroom,
|
||||
const size_t tailroom)
|
||||
{
|
||||
// sanity check
|
||||
if (src.size() > LZ4_MAX_INPUT_SIZE)
|
||||
OPENVPN_THROW(lz4_error, "compress buffer size=" << src.size() << " exceeds LZ4_MAX_INPUT_SIZE=" << LZ4_MAX_INPUT_SIZE);
|
||||
|
||||
// allocate dest buffer
|
||||
BufferPtr dest = new BufferAllocated(sizeof(std::uint32_t) + headroom + tailroom + LZ4_COMPRESSBOUND(src.size()), 0);
|
||||
dest->init_headroom(headroom);
|
||||
|
||||
// as a hint to receiver, write the decompressed size
|
||||
{
|
||||
const std::uint32_t size = htonl(src.size());
|
||||
dest->write(&size, sizeof(size));
|
||||
}
|
||||
|
||||
// compress
|
||||
const int comp_size = ::LZ4_compress_default((const char *)src.c_data(), (char *)dest->data_end(),
|
||||
(int)src.size(), (int)dest->remaining(tailroom));
|
||||
if (comp_size <= 0)
|
||||
OPENVPN_THROW(lz4_error, "LZ4_compress_default returned error status=" << comp_size);
|
||||
dest->inc_size(comp_size);
|
||||
return dest;
|
||||
}
|
||||
|
||||
inline BufferPtr decompress(const ConstBuffer& source,
|
||||
const size_t headroom,
|
||||
const size_t tailroom,
|
||||
size_t max_decompressed_size=LZ4_MAX_INPUT_SIZE)
|
||||
{
|
||||
// get the decompressed size
|
||||
ConstBuffer src(source);
|
||||
if (src.size() < sizeof(std::uint32_t))
|
||||
OPENVPN_THROW(lz4_error, "decompress buffer size=" << src.size() << " is too small");
|
||||
std::uint32_t size;
|
||||
src.read(&size, sizeof(size));
|
||||
size = ntohl(size);
|
||||
if (max_decompressed_size > LZ4_MAX_INPUT_SIZE)
|
||||
max_decompressed_size = LZ4_MAX_INPUT_SIZE;
|
||||
if (max_decompressed_size && size > max_decompressed_size)
|
||||
OPENVPN_THROW(lz4_error, "decompress expansion size=" << size << " is too large (must be <= " << max_decompressed_size << ')');
|
||||
|
||||
// allocate dest buffer
|
||||
BufferPtr dest = new BufferAllocated(headroom + tailroom + size, 0);
|
||||
dest->init_headroom(headroom);
|
||||
|
||||
// decompress
|
||||
const int decomp_size = LZ4_decompress_safe((const char *)src.c_data(), (char *)dest->data(),
|
||||
(int)src.size(), size);
|
||||
if (decomp_size <= 0)
|
||||
OPENVPN_THROW(lz4_error, "LZ4_decompress_safe returned error status=" << decomp_size);
|
||||
if (decomp_size != size)
|
||||
OPENVPN_THROW(lz4_error, "decompress size inconsistency expected_size=" << size << " actual_size=" << decomp_size);
|
||||
dest->inc_size(decomp_size);
|
||||
return dest;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,6 @@
|
||||
#ifndef OPENVPN_BUFFER_ZLIB_H
|
||||
#define OPENVPN_BUFFER_ZLIB_H
|
||||
|
||||
#ifdef OPENVPN_GZIP_DEBUG
|
||||
#define OPENVPN_GZIP_VERBOSE true
|
||||
#else
|
||||
#define OPENVPN_GZIP_VERBOSE false
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
|
||||
#include <cstring> // for std::memset
|
||||
@@ -57,7 +51,6 @@ namespace openvpn {
|
||||
const size_t headroom,
|
||||
const size_t tailroom,
|
||||
const int level,
|
||||
const bool verbose=OPENVPN_GZIP_VERBOSE,
|
||||
const int window_bits=15,
|
||||
const int mem_level=8)
|
||||
{
|
||||
@@ -90,8 +83,6 @@ namespace openvpn {
|
||||
if (status != Z_STREAM_END)
|
||||
OPENVPN_THROW(zlib_error, "zlib deflate failed, error=" << status);
|
||||
b->set_size(zs.s.total_out);
|
||||
if (verbose)
|
||||
OPENVPN_LOG("*** COMPRESS " << src->size() << " -> " << b->size());
|
||||
return b;
|
||||
}
|
||||
else
|
||||
@@ -102,7 +93,6 @@ namespace openvpn {
|
||||
const size_t headroom,
|
||||
const size_t tailroom,
|
||||
const size_t max_size,
|
||||
const bool verbose=OPENVPN_GZIP_VERBOSE,
|
||||
const size_t block_size=4096,
|
||||
const int window_bits=15)
|
||||
{
|
||||
@@ -142,8 +132,6 @@ namespace openvpn {
|
||||
OPENVPN_THROW(zlib_error, "zlib inflate max_size " << max_size << " exceeded");
|
||||
hr = tr = 0;
|
||||
} while (status == Z_OK);
|
||||
if (verbose)
|
||||
OPENVPN_LOG("*** DECOMPRESS " << src->size() << " -> " << blist.join_size());
|
||||
return blist.join(headroom, tailroom, true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -429,14 +429,18 @@ namespace openvpn {
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::DynamicChallenge(reason);
|
||||
client_options->events().add_event(std::move(ev));
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::AuthFailed(reason);
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::AUTH_FAILED);
|
||||
if (client_options->retry_on_auth_failed())
|
||||
queue_restart(5000);
|
||||
else
|
||||
stop();
|
||||
}
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
case Error::TUN_SETUP_FAILED:
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace openvpn {
|
||||
MAX_DIRECTIVE_SIZE=64, // maximum number of chars in an OpenVPN directive
|
||||
OPT_OVERHEAD=64, // bytes overhead of one option/directive, for accounting purposes
|
||||
TERM_OVERHEAD=16, // bytes overhead of one argument in an option, for accounting purposes
|
||||
MAX_SERVER_LIST_SIZE=4096, // maximum server list size, i.e. "setenv SERVER ..."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +275,8 @@ namespace openvpn {
|
||||
{
|
||||
std::ostringstream out;
|
||||
// eg. "godot@foo.bar.gov:443 (1.2.3.4) via TCPv4 on tun0/5.5.1.1"
|
||||
out << user << '@';
|
||||
if (!user.empty())
|
||||
out << user << '@';
|
||||
if (server_host.find_first_of(':') == std::string::npos)
|
||||
out << server_host;
|
||||
else
|
||||
|
||||
@@ -142,6 +142,7 @@ namespace openvpn {
|
||||
int default_key_direction = -1;
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
bool autologin_sessions = false;
|
||||
bool retry_on_auth_failed = false;
|
||||
std::string tls_version_min_override;
|
||||
std::string tls_cert_profile_override;
|
||||
PeerInfo::Set::Ptr extra_peer_info;
|
||||
@@ -193,7 +194,8 @@ namespace openvpn {
|
||||
autologin_sessions(false),
|
||||
creds_locked(false),
|
||||
asio_work_always_on_(false),
|
||||
synchronous_dns_lookup(false)
|
||||
synchronous_dns_lookup(false),
|
||||
retry_on_auth_failed_(config.retry_on_auth_failed)
|
||||
#ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY
|
||||
,extern_transport_factory(config.extern_transport_factory)
|
||||
#endif
|
||||
@@ -376,6 +378,8 @@ namespace openvpn {
|
||||
tunconf->tun_prop.remote_list = remote_list;
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_persist.reset(new TunLinux::TunPersist(true, false, nullptr));
|
||||
tunconf->load(opt);
|
||||
tun_factory = tunconf;
|
||||
}
|
||||
@@ -489,8 +493,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// show unused options
|
||||
if (opt.n_unused())
|
||||
OPENVPN_LOG(OPENVPN_UNUSED_OPTIONS << std::endl << opt.render(Option::RENDER_TRUNC_64|Option::RENDER_NUMBER|Option::RENDER_BRACKET|Option::RENDER_UNUSED));
|
||||
opt.show_unused_options(OPENVPN_UNUSED_OPTIONS);
|
||||
}
|
||||
|
||||
static PeerInfo::Set::Ptr build_peer_info(const Config& config, const ParseClientConfig& pcc, const bool autologin_sessions)
|
||||
@@ -548,6 +551,11 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retry_on_auth_failed() const
|
||||
{
|
||||
return retry_on_auth_failed_;
|
||||
}
|
||||
|
||||
Client::Config::Ptr client_config(const bool relay_mode)
|
||||
{
|
||||
Client::Config::Ptr cli_config = new Client::Config;
|
||||
@@ -836,6 +844,7 @@ namespace openvpn {
|
||||
bool creds_locked;
|
||||
bool asio_work_always_on_;
|
||||
bool synchronous_dns_lookup;
|
||||
bool retry_on_auth_failed_;
|
||||
PushOptionsBase::Ptr push_base;
|
||||
OptionList::FilterBase::Ptr pushed_options_filter;
|
||||
ClientLifeCycle::Ptr client_lifecycle;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAVE_CONFIG_JSONCPP
|
||||
#include "json/json.h"
|
||||
@@ -76,7 +77,7 @@ namespace openvpn {
|
||||
reset_pod();
|
||||
|
||||
// limits
|
||||
const size_t max_server_list_size = 64;
|
||||
const size_t max_server_list_size = ProfileParseLimits::MAX_SERVER_LIST_SIZE;
|
||||
|
||||
// setenv UV_x
|
||||
PeerInfo::Set::Ptr peer_info_uv(new PeerInfo::Set);
|
||||
@@ -123,7 +124,7 @@ namespace openvpn {
|
||||
se.friendlyName = slist[1];
|
||||
}
|
||||
if (!se.server.empty() && !se.friendlyName.empty() && serverList_.size() < max_server_list_size)
|
||||
serverList_.push_back(se);
|
||||
serverList_.push_back(std::move(se));
|
||||
}
|
||||
else if (arg1 == "PUSH_PEER_INFO")
|
||||
pushPeerInfo_ = true;
|
||||
@@ -260,7 +261,7 @@ namespace openvpn {
|
||||
Option::validate_string("HOST_LIST server", se.server, 256);
|
||||
Option::validate_string("HOST_LIST friendly name", se.friendlyName, 256);
|
||||
if (!se.server.empty() && !se.friendlyName.empty() && serverList_.size() < max_server_list_size)
|
||||
serverList_.push_back(se);
|
||||
serverList_.push_back(std::move(se));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,7 +347,7 @@ namespace openvpn {
|
||||
{
|
||||
Option opt;
|
||||
opt.push_back("client");
|
||||
options.push_back(opt);
|
||||
options.push_back(std::move(opt));
|
||||
added = true;
|
||||
}
|
||||
|
||||
@@ -356,7 +357,7 @@ namespace openvpn {
|
||||
Option opt;
|
||||
opt.push_back("dev");
|
||||
opt.push_back("tun");
|
||||
options.push_back(opt);
|
||||
options.push_back(std::move(opt));
|
||||
added = true;
|
||||
}
|
||||
if (added)
|
||||
|
||||
@@ -702,7 +702,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("Error parsing client-ip: " << e.what());
|
||||
OPENVPN_LOG("exception parsing client-ip: " << e.what());
|
||||
}
|
||||
ev->tun_name = tun->tun_name();
|
||||
connected_ = std::move(ev);
|
||||
@@ -873,7 +873,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("Error parsing inactive: " << e.what());
|
||||
OPENVPN_LOG("exception parsing inactive: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,9 +137,8 @@ namespace openvpn {
|
||||
|
||||
// cache a list of DNS-resolved IP addresses
|
||||
template <class EPRANGE>
|
||||
void set_endpoint_range(EPRANGE& endpoint_range, RandomAPI* rng)
|
||||
void set_endpoint_range(const EPRANGE& endpoint_range, RandomAPI* rng)
|
||||
{
|
||||
EPRANGE end;
|
||||
res_addr_list.reset(new ResolvedAddrList());
|
||||
for (const auto &i : endpoint_range)
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("ActionThread Exception: " << e.what());
|
||||
OPENVPN_LOG("ActionThread exception: " << e.what());
|
||||
}
|
||||
openvpn_io::post(io_context, [self=Ptr(this), status]()
|
||||
{
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
#ifndef OPENVPN_COMMON_APPVERSION_H
|
||||
#define OPENVPN_COMMON_APPVERSION_H
|
||||
|
||||
// VERSION version can be passed on build command line
|
||||
// BUILD_VERSION version can be passed on build command line
|
||||
|
||||
#include <openvpn/common/stringize.hpp>
|
||||
|
||||
#ifdef VERSION
|
||||
#define MY_VERSION OPENVPN_STRINGIZE(VERSION)
|
||||
#ifdef BUILD_VERSION
|
||||
#define MY_VERSION OPENVPN_STRINGIZE(BUILD_VERSION)
|
||||
#else
|
||||
#define MY_VERSION "0.1.0"
|
||||
#endif
|
||||
|
||||
@@ -34,10 +34,28 @@
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/uniqueptr.hpp>
|
||||
#include <openvpn/common/function.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
OPENVPN_EXCEPTION(enum_dir_error);
|
||||
|
||||
inline bool enum_dir(const std::string& dirname,
|
||||
Function<void(std::string fn)> 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)
|
||||
@@ -45,23 +63,18 @@ namespace openvpn {
|
||||
std::vector<std::string> ret;
|
||||
if (size_hint)
|
||||
ret.reserve(size_hint);
|
||||
unique_ptr_del<DIR> dir(::opendir(dirname.c_str()), [](DIR* d) { ::closedir(d); });
|
||||
if (!dir)
|
||||
throw enum_dir_error(dirname + ": cannot open directory");
|
||||
|
||||
struct dirent *e;
|
||||
while ((e = ::readdir(dir.get())) != nullptr)
|
||||
{
|
||||
std::string fn(e->d_name);
|
||||
if (fn != "." && fn != "..")
|
||||
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
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace openvpn {
|
||||
inline void write_binary_atomic(const std::string& fn,
|
||||
const std::string& tmpdir,
|
||||
const mode_t mode,
|
||||
const Buffer& buf,
|
||||
const ConstBuffer& buf,
|
||||
RandomAPI& rng)
|
||||
{
|
||||
// generate temporary filename
|
||||
@@ -65,6 +65,15 @@ namespace openvpn {
|
||||
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 Buffer& buf,
|
||||
RandomAPI& rng)
|
||||
{
|
||||
return write_binary_atomic(fn, tmpdir, mode, const_buffer_ref(buf), rng);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -82,6 +82,13 @@ namespace openvpn {
|
||||
write_binary_unix(fn, mode, buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
inline void write_binary_unix(const std::string& fn,
|
||||
const mode_t mode,
|
||||
const ConstBuffer& buf)
|
||||
{
|
||||
write_binary_unix(fn, mode, buf.c_data(), buf.size());
|
||||
}
|
||||
|
||||
inline void write_text_unix(const std::string& fn,
|
||||
const mode_t mode,
|
||||
const std::string& content)
|
||||
@@ -140,6 +147,19 @@ namespace openvpn {
|
||||
return bp;
|
||||
}
|
||||
|
||||
inline bool 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)
|
||||
|
||||
@@ -85,6 +85,23 @@ namespace openvpn {
|
||||
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))
|
||||
@@ -95,8 +112,11 @@ namespace openvpn {
|
||||
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))
|
||||
@@ -118,7 +138,10 @@ namespace openvpn {
|
||||
if (host.length() >= 2 && host[0] == '[' && host[host.length()-1] == ']')
|
||||
host = host.substr(1, host.length()-2);
|
||||
|
||||
return is_valid_host(host) && is_valid_port(port, port_save);
|
||||
if (allow_unix && port == "unix")
|
||||
return is_valid_unix_sock(host);
|
||||
else
|
||||
return is_valid_host(host) && is_valid_port(port, port_save);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace openvpn {
|
||||
Option(T first, Args... args)
|
||||
{
|
||||
reserve(1 + sizeof...(args));
|
||||
from_list(first, args...);
|
||||
from_list(std::move(first), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static validate_status validate(const std::string& str, const size_t max_len)
|
||||
@@ -360,8 +360,8 @@ namespace openvpn {
|
||||
template<typename T, typename... Args>
|
||||
void from_list(T first, Args... args)
|
||||
{
|
||||
from_list(first);
|
||||
from_list(args...);
|
||||
from_list(std::move(first));
|
||||
from_list(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
volatile mutable bool touched_ = false;
|
||||
@@ -661,6 +661,18 @@ namespace openvpn {
|
||||
}
|
||||
};
|
||||
|
||||
OptionList()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
OptionList(T first, Args... args)
|
||||
{
|
||||
reserve(1 + sizeof...(args));
|
||||
from_list(std::move(first), std::forward<Args>(args)...);
|
||||
update_map();
|
||||
}
|
||||
|
||||
static OptionList parse_from_csv_static(const std::string& str, Limits* lim)
|
||||
{
|
||||
OptionList ret;
|
||||
@@ -1268,6 +1280,17 @@ namespace openvpn {
|
||||
return n;
|
||||
}
|
||||
|
||||
void show_unused_options(const char *title=nullptr) const
|
||||
{
|
||||
// show unused options
|
||||
if (n_unused())
|
||||
{
|
||||
if (!title)
|
||||
title = "NOTE: Unused Options";
|
||||
OPENVPN_LOG_NTNL(title << std::endl << render(Option::RENDER_TRUNC_64|Option::RENDER_NUMBER|Option::RENDER_BRACKET|Option::RENDER_UNUSED));
|
||||
}
|
||||
}
|
||||
|
||||
// Add item to underlying option list while updating map as well.
|
||||
void add_item(const Option& opt)
|
||||
{
|
||||
@@ -1401,6 +1424,18 @@ namespace openvpn {
|
||||
OPENVPN_THROW(option_error, "line " << line_num << " is too long");
|
||||
}
|
||||
|
||||
void from_list(Option opt)
|
||||
{
|
||||
push_back(std::move(opt));
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void from_list(T first, Args... args)
|
||||
{
|
||||
from_list(std::move(first));
|
||||
from_list(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
IndexMap map_;
|
||||
};
|
||||
|
||||
|
||||
@@ -118,11 +118,13 @@ namespace openvpn {
|
||||
const Argv& argv,
|
||||
const Environ* env,
|
||||
RedirectPipe::InOut& inout,
|
||||
const bool combine_out_err)
|
||||
unsigned int redirect_pipe_flags)
|
||||
{
|
||||
SignalBlockerPipe sbpipe;
|
||||
RedirectPipe remote;
|
||||
RedirectPipe local(remote, combine_out_err, !inout.in.empty());
|
||||
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;
|
||||
@@ -159,7 +161,7 @@ namespace openvpn {
|
||||
os << "Error: command failed to execute" << std::endl;
|
||||
#else
|
||||
RedirectPipe::InOut inout;
|
||||
const int status = system_cmd(argv[0], argv, nullptr, inout, true);
|
||||
const int status = system_cmd(argv[0], argv, nullptr, inout, RedirectPipe::COMBINE_OUT_ERR);
|
||||
if (status < 0)
|
||||
os << "Error: command failed to execute" << std::endl;
|
||||
os << inout.out;
|
||||
|
||||
+60
-29
@@ -202,6 +202,14 @@ namespace openvpn {
|
||||
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;
|
||||
@@ -212,40 +220,50 @@ namespace openvpn {
|
||||
RedirectPipe() {}
|
||||
|
||||
RedirectPipe(RedirectStdFD& remote,
|
||||
const bool combine_out_err_arg,
|
||||
const bool enable_in)
|
||||
const unsigned int flags_arg)
|
||||
: flags(flags_arg)
|
||||
{
|
||||
int fd[2];
|
||||
|
||||
// stdout
|
||||
Pipe::make_pipe(fd);
|
||||
out.reset(cloexec(fd[0]));
|
||||
remote.out.reset(fd[1]);
|
||||
if (!(flags & IGNORE_OUT))
|
||||
{
|
||||
int fd[2];
|
||||
Pipe::make_pipe(fd);
|
||||
out.reset(cloexec(fd[0]));
|
||||
remote.out.reset(fd[1]);
|
||||
}
|
||||
|
||||
// stderr
|
||||
combine_out_err = remote.combine_out_err = combine_out_err_arg;
|
||||
if (!combine_out_err)
|
||||
if (!(flags & IGNORE_ERR))
|
||||
{
|
||||
Pipe::make_pipe(fd);
|
||||
err.reset(cloexec(fd[0]));
|
||||
remote.err.reset(fd[1]);
|
||||
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 (enable_in)
|
||||
if (!(flags & IGNORE_IN))
|
||||
{
|
||||
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())
|
||||
if (flags & ENABLE_IN)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(redirect_std_err, "error opening /dev/null : " << strerror_str(eno));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,12 +271,24 @@ namespace openvpn {
|
||||
void transact(InOut& inout)
|
||||
{
|
||||
openvpn_io::io_context io_context(1);
|
||||
Pipe::SD_OUT send_in(io_context, inout.in, in);
|
||||
Pipe::SD_IN recv_out(io_context, out);
|
||||
Pipe::SD_IN recv_err(io_context, err);
|
||||
|
||||
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();
|
||||
inout.out = recv_out.content();
|
||||
inout.err = recv_err.content();
|
||||
|
||||
if (recv_out)
|
||||
inout.out = recv_out->content();
|
||||
if (recv_err)
|
||||
inout.err = recv_err->content();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -273,6 +303,7 @@ namespace openvpn {
|
||||
return fd;
|
||||
}
|
||||
|
||||
const unsigned int flags = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +222,16 @@ namespace openvpn {
|
||||
return str.find_first_of('\n') != std::string::npos;
|
||||
}
|
||||
|
||||
// return the first line (without newline) of a multi-line string
|
||||
std::string first_line(const std::string& str)
|
||||
{
|
||||
const size_t pos = str.find_first_of('\n');
|
||||
if (pos != std::string::npos)
|
||||
return str.substr(0, pos);
|
||||
else
|
||||
return str;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
#ifndef OPENVPN_COMMON_VERSION_H
|
||||
#define OPENVPN_COMMON_VERSION_H
|
||||
|
||||
#define OPENVPN_VERSION "3.1.2"
|
||||
#define OPENVPN_VERSION "3.git:master"
|
||||
|
||||
#endif // OPENVPN_COMMON_VERSION_H
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace openvpn {
|
||||
template <typename THREAD_COMMON>
|
||||
inline void event_loop_wait_barrier(THREAD_COMMON& tc,
|
||||
const unsigned int seconds=10)
|
||||
const unsigned int seconds=30)
|
||||
{
|
||||
// barrier prior to event-loop entry
|
||||
switch (tc.event_loop_bar.wait(seconds))
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace openvpn {
|
||||
class StaticKey
|
||||
{
|
||||
friend class OpenVPNStaticKey;
|
||||
typedef BufferAllocatedType<unsigned char> key_t;
|
||||
typedef BufferAllocated key_t;
|
||||
|
||||
public:
|
||||
StaticKey() {}
|
||||
|
||||
@@ -43,6 +43,22 @@ namespace openvpn {
|
||||
pthread_t current_thread = pthread_self();
|
||||
return pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);
|
||||
}
|
||||
|
||||
inline int exclude_from_core(const int core_id)
|
||||
{
|
||||
const int num_cores = n_cores();
|
||||
if (num_cores <= 1 || core_id >= num_cores)
|
||||
return EINVAL;
|
||||
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
for (int i = 0; i < num_cores; ++i)
|
||||
if (i != core_id)
|
||||
CPU_SET(i, &cpuset);
|
||||
|
||||
pthread_t current_thread = pthread_self();
|
||||
return pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -687,16 +687,26 @@ namespace openvpn {
|
||||
clear();
|
||||
try {
|
||||
const Config& c = *ctx->config;
|
||||
int status;
|
||||
int endpoint, status;
|
||||
|
||||
// set pointer back to parent
|
||||
parent = ctx;
|
||||
|
||||
// set client/server mode
|
||||
if (c.mode.is_server())
|
||||
{
|
||||
endpoint = MBEDTLS_SSL_IS_SERVER;
|
||||
authcert.reset(new AuthCert());
|
||||
}
|
||||
else if (c.mode.is_client())
|
||||
endpoint = MBEDTLS_SSL_IS_CLIENT;
|
||||
else
|
||||
throw MbedTLSException("unknown client/server mode");
|
||||
|
||||
// init SSL configuration object
|
||||
sslconf = new mbedtls_ssl_config;
|
||||
mbedtls_ssl_config_init(sslconf);
|
||||
mbedtls_ssl_config_defaults(sslconf,
|
||||
c.mode.is_client() ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
|
||||
mbedtls_ssl_config_defaults(sslconf, endpoint,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT);
|
||||
|
||||
@@ -707,17 +717,6 @@ namespace openvpn {
|
||||
ssl = new mbedtls_ssl_context;
|
||||
mbedtls_ssl_init(ssl);
|
||||
|
||||
// set client/server mode
|
||||
if (c.mode.is_server())
|
||||
{
|
||||
mbedtls_ssl_conf_endpoint(sslconf, MBEDTLS_SSL_IS_SERVER);
|
||||
authcert.reset(new AuthCert());
|
||||
}
|
||||
else if (c.mode.is_client())
|
||||
mbedtls_ssl_conf_endpoint(sslconf, MBEDTLS_SSL_IS_CLIENT);
|
||||
else
|
||||
throw MbedTLSException("unknown client/server mode");
|
||||
|
||||
// set minimum TLS version
|
||||
if (!c.force_aes_cbc_ciphersuites || c.tls_version_min > TLSVersion::UNDEF)
|
||||
{
|
||||
@@ -1345,7 +1344,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MbedTLSContext::epki_sign: " << e.what());
|
||||
OPENVPN_LOG("MbedTLSContext::epki_sign exception: " << e.what());
|
||||
return MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace openvpn {
|
||||
argv.emplace_back("-6");
|
||||
argv.emplace_back("route");
|
||||
argv.emplace_back("show");
|
||||
const int status = system_cmd(argv[0], argv, nullptr, pipe, false);
|
||||
const int status = system_cmd(argv[0], argv, nullptr, pipe, 0);
|
||||
if (status != 0)
|
||||
OPENVPN_THROW(linux_gw_error, "command returned error status " << status << " : " << argv.to_string());
|
||||
return pipe.out;
|
||||
|
||||
@@ -522,6 +522,9 @@ namespace openvpn {
|
||||
if (!ssl)
|
||||
throw OpenSSLException("OpenSSLContext::SSL: SSL_new failed");
|
||||
|
||||
// release unneeded buffers
|
||||
SSL_set_mode(ssl, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
// verify hostname
|
||||
if (hostname)
|
||||
{
|
||||
@@ -791,7 +794,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("OpenSSLContext::ExternalPKIImpl::rsa_priv_enc: " << e.what());
|
||||
OPENVPN_LOG("OpenSSLContext::ExternalPKIImpl::rsa_priv_enc exception: " << e.what());
|
||||
++self->n_errors;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ namespace openvpn {
|
||||
SSLUnspecified,
|
||||
SSLOn,
|
||||
SSLOff,
|
||||
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
|
||||
AltRouting,
|
||||
#endif
|
||||
};
|
||||
|
||||
std::string directive;
|
||||
@@ -60,10 +63,22 @@ namespace openvpn {
|
||||
if (!proto.is_local())
|
||||
ret += ' ' + port;
|
||||
ret += ' ' + std::string(proto.str()) + ' ' + openvpn::to_string(n_threads);
|
||||
if (ssl == SSLOn)
|
||||
ret += " ssl";
|
||||
else if (ssl == SSLOff)
|
||||
ret += " !ssl";
|
||||
switch (ssl)
|
||||
{
|
||||
case SSLUnspecified:
|
||||
break;
|
||||
case SSLOn:
|
||||
ret += " ssl";
|
||||
break;
|
||||
case SSLOff:
|
||||
ret += " !ssl";
|
||||
break;
|
||||
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
|
||||
case AltRouting:
|
||||
ret += " alt";
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -192,6 +207,10 @@ namespace openvpn {
|
||||
}
|
||||
else if (ssl_qualifier == "!ssl")
|
||||
e.ssl = Item::SSLOff;
|
||||
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
|
||||
else if (ssl_qualifier == "alt")
|
||||
e.ssl = Item::AltRouting;
|
||||
#endif
|
||||
else
|
||||
OPENVPN_THROW(option_error, e.directive << ": unrecognized SSL qualifier");
|
||||
}
|
||||
|
||||
@@ -87,6 +87,9 @@ namespace openvpn {
|
||||
const std::string* username,
|
||||
const bool challenge,
|
||||
const bool throw_on_error) = 0;
|
||||
|
||||
// notify of local user properties update
|
||||
virtual void userprop_local_update() = 0;
|
||||
};
|
||||
|
||||
// Base class for the client instance receiver. Note that all
|
||||
@@ -113,6 +116,9 @@ namespace openvpn {
|
||||
// send control channel message
|
||||
virtual void post_cc_msg(BufferPtr&& msg) = 0;
|
||||
|
||||
// schedule a low-level connection disconnect in seconds
|
||||
virtual void schedule_disconnect(const unsigned int seconds) = 0;
|
||||
|
||||
// set up relay to target
|
||||
virtual void relay(const IP::Addr& target, const int port) = 0;
|
||||
|
||||
|
||||
@@ -327,12 +327,12 @@ namespace openvpn {
|
||||
const AuthCert::Ptr& auth_cert) override
|
||||
{
|
||||
constexpr size_t MAX_USERNAME_SIZE = 256;
|
||||
constexpr size_t MAX_PASSWORD_SIZE = 256;
|
||||
constexpr size_t MAX_PASSWORD_SIZE = 16384;
|
||||
|
||||
if (get_management())
|
||||
{
|
||||
AuthCreds::Ptr auth_creds(new AuthCreds(Unicode::utf8_printable(username, MAX_USERNAME_SIZE|Unicode::UTF8_FILTER),
|
||||
Unicode::utf8_printable(password, MAX_PASSWORD_SIZE|Unicode::UTF8_FILTER),
|
||||
Unicode::utf8_printable(password, MAX_PASSWORD_SIZE|Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT),
|
||||
Unicode::utf8_printable(peer_info, Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT)));
|
||||
ManLink::send->auth_request(auth_creds, auth_cert, peer_addr);
|
||||
}
|
||||
@@ -512,6 +512,15 @@ namespace openvpn {
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void schedule_disconnect(const unsigned int seconds)
|
||||
{
|
||||
if (halt || did_client_halt_restart)
|
||||
return;
|
||||
Base::update_now();
|
||||
disconnect_in(Time::Duration::seconds(seconds));
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void post_cc_msg(BufferPtr&& msg) override
|
||||
{
|
||||
if (halt || !Base::primary_defined())
|
||||
@@ -621,6 +630,7 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
housekeeping_timer.cancel();
|
||||
housekeeping_schedule.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-4
@@ -338,6 +338,10 @@ namespace openvpn {
|
||||
|
||||
// Compatibility
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
|
||||
// For compatibility with openvpn2 we send initial options on rekeying,
|
||||
// instead of possible modifications caused by NCP
|
||||
std::string initial_options;
|
||||
|
||||
void load(const OptionList& opt, const ProtoContextOptions& pco,
|
||||
const int default_key_direction, const bool server)
|
||||
@@ -669,6 +673,9 @@ namespace openvpn {
|
||||
// transmitted to peer for options consistency check
|
||||
std::string options_string()
|
||||
{
|
||||
if (!initial_options.empty())
|
||||
return initial_options;
|
||||
|
||||
std::ostringstream out;
|
||||
|
||||
const bool server = ssl_factory->mode().is_server();
|
||||
@@ -708,8 +715,10 @@ namespace openvpn {
|
||||
out << ",tls-server";
|
||||
else
|
||||
out << ",tls-client";
|
||||
|
||||
initial_options = out.str();
|
||||
|
||||
return out.str();
|
||||
return initial_options;
|
||||
}
|
||||
|
||||
// generate a string summarizing information about the client
|
||||
@@ -1593,8 +1602,9 @@ namespace openvpn {
|
||||
return validate_tls_plain(recv, proto, now);
|
||||
}
|
||||
}
|
||||
catch (BufferException&)
|
||||
catch (BufferException& e)
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE("validate() exception: " << e.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1764,9 +1774,9 @@ namespace openvpn {
|
||||
// verify tls_auth packet ID
|
||||
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
|
||||
// make sure that our own PSID is contained in packet received from peer
|
||||
if (ReliableAck::ack_skip(recv))
|
||||
if (ReliableAck::ack_skip(work))
|
||||
{
|
||||
ProtoSessionID dest_psid(recv);
|
||||
ProtoSessionID dest_psid(work);
|
||||
if (!proto.psid_self.match(dest_psid))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -304,16 +304,15 @@ namespace openvpn {
|
||||
frame_context.prepare(tcpfrom->buf);
|
||||
|
||||
socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf),
|
||||
[self=Ptr(this), tcpfrom](const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
[self=Ptr(this), tcpfrom=PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
|
||||
{
|
||||
self->handle_recv(tcpfrom, error, bytes_recvd);
|
||||
self->handle_recv(std::move(tcpfrom), error, bytes_recvd);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_recv(PacketFrom *tcpfrom, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("TCPLink::handle_recv: " << error.message());
|
||||
PacketFrom::SPtr pfp(tcpfrom);
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
@@ -331,7 +330,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract error: " << e.what());
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what());
|
||||
stats->error(Error::TCP_SIZE_ERROR);
|
||||
read_handler->tcp_error_handler("TCP_SIZE_ERROR");
|
||||
stop();
|
||||
|
||||
@@ -141,16 +141,15 @@ namespace openvpn {
|
||||
frame_context.prepare(udpfrom->buf);
|
||||
socket.async_receive_from(frame_context.mutable_buffer(udpfrom->buf),
|
||||
udpfrom->sender_endpoint,
|
||||
[self=Ptr(this), udpfrom](const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
[self=Ptr(this), udpfrom=PacketFrom::SPtr(udpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
|
||||
{
|
||||
self->handle_read(udpfrom, error, bytes_recvd);
|
||||
self->handle_read(std::move(udpfrom), error, bytes_recvd);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_read(PacketFrom *udpfrom, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
void handle_read(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_VERBOSE("UDPLink::handle_read: " << error.message());
|
||||
PacketFrom::SPtr pfp(udpfrom);
|
||||
if (!halt)
|
||||
{
|
||||
if (bytes_recvd)
|
||||
@@ -200,7 +199,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_ERROR("UDP send error: " << e.what());
|
||||
OPENVPN_LOG_UDPLINK_ERROR("UDP send exception: " << e.what());
|
||||
stats->error(Error::NETWORK_SEND_ERROR);
|
||||
return e.code().value();
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error processing route-metric: " << e.what());
|
||||
OPENVPN_LOG("exception processing route-metric: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error parsing IPv4 route: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
OPENVPN_LOG("exception parsing IPv4 route: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,7 +424,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error parsing IPv6 route: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
OPENVPN_LOG("exception parsing IPv6 route: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,7 +451,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error adding remote bypass route: " << addr.to_string() << " : " << e.what());
|
||||
OPENVPN_LOG("exception adding remote bypass route: " << addr.to_string() << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -561,7 +561,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error parsing dhcp-option: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
OPENVPN_LOG("exception parsing dhcp-option: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
try {
|
||||
@@ -584,7 +584,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error setting dhcp-option for proxy: " << e.what());
|
||||
OPENVPN_LOG("exception setting dhcp-option for proxy: " << e.what());
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
@@ -606,7 +606,7 @@ namespace openvpn {
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (!quiet)
|
||||
OPENVPN_LOG("Error parsing dhcp-option: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
OPENVPN_LOG("exception parsing dhcp-option: " << o.render(OPT_RENDER_FLAGS) << " : " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+114
-321
@@ -24,307 +24,49 @@
|
||||
#ifndef OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
#define OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/linux/tun.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/common/cleanup.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
template <typename ReadHandler>
|
||||
class Tun : public TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor>
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
typedef TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor> Base;
|
||||
|
||||
inline IP::Addr get_default_gateway_v4()
|
||||
{
|
||||
typedef std::vector<std::string> strvec;
|
||||
const std::string proc_net_route = read_text_simple("/proc/net/route");
|
||||
SplitLines in(proc_net_route, 0);
|
||||
while (in(true))
|
||||
{
|
||||
const std::string& line = in.line_ref();
|
||||
strvec v = Split::by_space<strvec, StandardLex, SpaceMatch, Split::NullLimit>(line);
|
||||
if (v.size() >= 8)
|
||||
{
|
||||
if (v[1] == "00000000" && v[7] == "00000000")
|
||||
{
|
||||
const IP::Addr gw = cvt_pnr_ip_v4(v[2]);
|
||||
return gw;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw tun_linux_error("can't determine default gateway");
|
||||
}
|
||||
public:
|
||||
typedef RCPtr<Tun> Ptr;
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Tun(openvpn_io::io_context& io_context,
|
||||
ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg,
|
||||
const int socket,
|
||||
const std::string& name)
|
||||
: Base(read_handler_arg, frame_arg, stats_arg)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
const IP::Addr gw4 = get_default_gateway_v4();
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
Base::name_ = name;
|
||||
Base::retain_stream = true;
|
||||
Base::stream = new openvpn_io::posix::stream_descriptor(io_context, socket);
|
||||
OPENVPN_LOG_TUN(Base::name_ << " opened");
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw4.to_string(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
~Tun() { Base::stop(); }
|
||||
};
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw4.to_string(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
add_del_route("0000::", 1, local6->gateway, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
typedef TunPersistTemplate<ScopedFD> TunPersist;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
@@ -340,6 +82,9 @@ namespace openvpn {
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
TunBuilderSetup::Factory::Ptr tun_setup_factory;
|
||||
TunPersist::Ptr tun_persist;
|
||||
|
||||
void load(const OptionList& opt)
|
||||
{
|
||||
// set a default MTU
|
||||
@@ -363,6 +108,15 @@ namespace openvpn {
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli);
|
||||
|
||||
TunBuilderSetup::Base::Ptr new_setup_obj()
|
||||
{
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj();
|
||||
else
|
||||
return new TunLinux::Setup();
|
||||
}
|
||||
|
||||
private:
|
||||
ClientConfig() {}
|
||||
};
|
||||
@@ -380,54 +134,94 @@ namespace openvpn {
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
|
||||
if (config->tun_persist)
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: long-term session scope");
|
||||
tun_persist = config->tun_persist; // long-term persistent
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("TunPersist: short-term connection scope");
|
||||
tun_persist.reset(new TunPersist(true, false, nullptr)); // short-term
|
||||
}
|
||||
|
||||
try {
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
int sd = -1;
|
||||
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
nullptr,
|
||||
false);
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
state = tun_persist->state();
|
||||
sd = tun_persist->obj();
|
||||
state = tun_persist->state();
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
}
|
||||
else
|
||||
{
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
// close old tun handle if persisted
|
||||
tun_persist->close();
|
||||
|
||||
// configure tun/tap interface properties
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionList());
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
nullptr,
|
||||
false);
|
||||
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
tun_setup = config->new_setup_obj();
|
||||
|
||||
// create config object for tun setup layer
|
||||
Setup::Config tsconf;
|
||||
tsconf.layer = config->tun_prop.layer;
|
||||
tsconf.dev_name = config->dev_name;
|
||||
tsconf.txqueuelen = config->txqueuelen;
|
||||
|
||||
// open/config tun
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
|
||||
sd = tun_setup->establish(*po, &tsconf, nullptr, os);
|
||||
}
|
||||
|
||||
// persist tun settings state
|
||||
state->iface_name = tsconf.iface_name;
|
||||
tun_persist->persist_tun_state(sd, state);
|
||||
|
||||
// enable tun_setup destructor
|
||||
tun_persist->add_destructor(tun_setup);
|
||||
}
|
||||
|
||||
// start tun
|
||||
impl.reset(new TunImpl(io_context,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats,
|
||||
config->dev_name,
|
||||
config->tun_prop.layer,
|
||||
config->txqueuelen
|
||||
sd,
|
||||
state->iface_name
|
||||
));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
// get the iface name
|
||||
state->iface_name = impl->name();
|
||||
|
||||
// configure tun properties
|
||||
TunLinux::tun_config(state->iface_name, *po, nullptr, *add_cmds, *remove_cmds);
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
|
||||
// signal that we are connected
|
||||
parent.tun_connected();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (tun_persist)
|
||||
tun_persist->close();
|
||||
|
||||
stop();
|
||||
parent.tun_error(Error::TUN_SETUP_FAILED, e.what());
|
||||
}
|
||||
@@ -522,22 +316,21 @@ namespace openvpn {
|
||||
{
|
||||
halt = true;
|
||||
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
|
||||
// stop tun
|
||||
if (impl)
|
||||
impl->stop();
|
||||
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist;
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunImpl::Ptr impl;
|
||||
TunProp::State::Ptr state;
|
||||
ActionList::Ptr remove_cmds;
|
||||
TunBuilderSetup::Base::Ptr tun_setup;
|
||||
bool halt;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,473 @@
|
||||
// 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/>.
|
||||
|
||||
// Client tun interface for Linux.
|
||||
|
||||
#ifndef OPENVPN_TUN_LINUX_CLIENT_TUNSETUP_H
|
||||
#define OPENVPN_TUN_LINUX_CLIENT_TUNSETUP_H
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
OPENVPN_EXCEPTION(tun_layer_error);
|
||||
OPENVPN_EXCEPTION(tun_ioctl_error);
|
||||
OPENVPN_EXCEPTION(tun_fcntl_error);
|
||||
OPENVPN_EXCEPTION(tun_name_error);
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
const LinuxGW46 gw(true);
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["iface_name"] = Json::Value(iface_name);
|
||||
root["layer"] = Json::Value(layer.str());
|
||||
root["dev_name"] = Json::Value(dev_name);
|
||||
root["txqueuelen"] = Json::Value(txqueuelen);
|
||||
return root;
|
||||
};
|
||||
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) override
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, iface_name, "iface_name", title);
|
||||
layer = Layer::from_str(json::get_string(root, "layer", title));
|
||||
json::to_string(root, dev_name, "dev_name", title);
|
||||
json::to_int(root, txqueuelen, "txqueuelen", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os)
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
if (!conf)
|
||||
throw tun_linux_error("missing config");
|
||||
|
||||
static const char node[] = "/dev/net/tun";
|
||||
ScopedFD fd(open(node, O_RDWR));
|
||||
if (!fd.defined())
|
||||
OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
|
||||
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_ONE_QUEUE;
|
||||
ifr.ifr_flags |= IFF_NO_PI;
|
||||
if (conf->layer() == Layer::OSI_LAYER_3)
|
||||
ifr.ifr_flags |= IFF_TUN;
|
||||
else if (conf->layer() == Layer::OSI_LAYER_2)
|
||||
ifr.ifr_flags |= IFF_TAP;
|
||||
else
|
||||
throw tun_layer_error("unknown OSI layer");
|
||||
|
||||
open_unit(conf->dev_name, ifr, fd);
|
||||
|
||||
if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_fcntl_error(errinfo(errno));
|
||||
|
||||
// Set the TX send queue size
|
||||
if (conf->txqueuelen)
|
||||
{
|
||||
struct ifreq netifr;
|
||||
ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0));
|
||||
|
||||
if (ctl_fd.defined())
|
||||
{
|
||||
std::memset(&netifr, 0, sizeof(netifr));
|
||||
strcpy (netifr.ifr_name, ifr.ifr_name);
|
||||
netifr.ifr_qlen = conf->txqueuelen;
|
||||
if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0)
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
else
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order
|
||||
|
||||
// configure tun properties
|
||||
tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds);
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
private:
|
||||
void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
const int max_units = 256;
|
||||
for (int unit = 0; unit < max_units; ++unit)
|
||||
{
|
||||
std::string n = name;
|
||||
if (unit)
|
||||
n += openvpn::to_string(unit);
|
||||
if (n.length() < IFNAMSIZ)
|
||||
::strcpy (ifr.ifr_name, n.c_str());
|
||||
else
|
||||
throw tun_name_error();
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0)
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionListReversed::Ptr remove_cmds;
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
|
||||
@@ -1,162 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
// Low-level tun interface driver for Linux, client/server independent.
|
||||
|
||||
#ifndef OPENVPN_TUN_LINUX_TUN_H
|
||||
#define OPENVPN_TUN_LINUX_TUN_H
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
// exceptions
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
OPENVPN_EXCEPTION(tun_layer_error);
|
||||
OPENVPN_EXCEPTION(tun_ioctl_error);
|
||||
OPENVPN_EXCEPTION(tun_fcntl_error);
|
||||
OPENVPN_EXCEPTION(tun_name_error);
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
|
||||
template <typename ReadHandler>
|
||||
class Tun : public TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor>
|
||||
{
|
||||
typedef TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor> Base;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Tun> Ptr;
|
||||
|
||||
Tun(openvpn_io::io_context& io_context,
|
||||
ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg,
|
||||
const std::string& name,
|
||||
const Layer& layer,
|
||||
const int txqueuelen)
|
||||
: Base(read_handler_arg, frame_arg, stats_arg)
|
||||
{
|
||||
static const char node[] = "/dev/net/tun";
|
||||
ScopedFD fd(open(node, O_RDWR));
|
||||
if (!fd.defined())
|
||||
OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
|
||||
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_ONE_QUEUE;
|
||||
ifr.ifr_flags |= IFF_NO_PI;
|
||||
if (layer() == Layer::OSI_LAYER_3)
|
||||
ifr.ifr_flags |= IFF_TUN;
|
||||
else if (layer() == Layer::OSI_LAYER_2)
|
||||
ifr.ifr_flags |= IFF_TAP;
|
||||
else
|
||||
throw tun_layer_error("unknown OSI layer");
|
||||
|
||||
open_unit(name, ifr, fd);
|
||||
|
||||
if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_fcntl_error(errinfo(errno));
|
||||
|
||||
// Set the TX send queue size
|
||||
if (txqueuelen)
|
||||
{
|
||||
struct ifreq netifr;
|
||||
ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0));
|
||||
|
||||
if (ctl_fd.defined())
|
||||
{
|
||||
std::memset(&netifr, 0, sizeof(netifr));
|
||||
strcpy (netifr.ifr_name, ifr.ifr_name);
|
||||
netifr.ifr_qlen = txqueuelen;
|
||||
if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0)
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
else
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
|
||||
Base::name_ = ifr.ifr_name;
|
||||
Base::stream = new openvpn_io::posix::stream_descriptor(io_context, fd.release());
|
||||
OPENVPN_LOG_TUN(Base::name_ << " opened");
|
||||
}
|
||||
|
||||
~Tun() { Base::stop(); }
|
||||
|
||||
private:
|
||||
static void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
const int max_units = 256;
|
||||
for (int unit = 0; unit < max_units; ++unit)
|
||||
{
|
||||
std::string n = name;
|
||||
if (unit)
|
||||
n += openvpn::to_string(unit);
|
||||
if (n.length() < IFNAMSIZ)
|
||||
::strcpy (ifr.ifr_name, n.c_str());
|
||||
else
|
||||
throw tun_name_error();
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0)
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TUN_LINUX_TUN_H
|
||||
@@ -217,7 +217,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNS: setdns: " << e.what());
|
||||
OPENVPN_LOG("MacDNS: setdns exception: " << e.what());
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
@@ -248,7 +248,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNS: resetdns: " << e.what());
|
||||
OPENVPN_LOG("MacDNS: resetdns exception: " << e.what());
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNSWatchdog::thread_func: " << e.what());
|
||||
OPENVPN_LOG("MacDNSWatchdog::thread_func exception: " << e.what());
|
||||
}
|
||||
cancel_push_timer();
|
||||
}
|
||||
@@ -272,7 +272,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("MacDNSWatchdog::push_timer_callback: " << e.what());
|
||||
OPENVPN_LOG("MacDNSWatchdog::push_timer_callback exception: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("*** TUN BUILDER CAPTURE ERROR: " << e.what());
|
||||
OPENVPN_LOG("*** TUN BUILDER CAPTURE exception: " << e.what());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("TunWrap destructor: " << e.what());
|
||||
OPENVPN_LOG("TunWrap destructor exception: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write error: " << e.what());
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &e.code());
|
||||
return false;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ namespace openvpn {
|
||||
}
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write error: " << e.what());
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &e.code());
|
||||
return false;
|
||||
}
|
||||
@@ -206,16 +206,15 @@ namespace openvpn {
|
||||
|
||||
// queue read on tun device
|
||||
stream->async_read_some(frame_context.mutable_buffer(tunfrom->buf),
|
||||
[self=Ptr(this), tunfrom](const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
[self=Ptr(this), tunfrom=typename PacketFrom::SPtr(tunfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
|
||||
{
|
||||
self->handle_read(tunfrom, error, bytes_recvd);
|
||||
self->handle_read(std::move(tunfrom), error, bytes_recvd);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_read(PacketFrom *tunfrom, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
void handle_read(typename PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
{
|
||||
OPENVPN_LOG_TUN_VERBOSE("TunIO::handle_read: " << error.message());
|
||||
typename PacketFrom::SPtr pfp(tunfrom);
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
|
||||
Reference in New Issue
Block a user