mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_SERVER_LISTENLIST_H
|
||||
#define OPENVPN_SERVER_LISTENLIST_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility> // for std::move
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Listen {
|
||||
struct Item
|
||||
{
|
||||
enum SSLMode {
|
||||
SSLUnspecified,
|
||||
SSLOn,
|
||||
SSLOff,
|
||||
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
|
||||
AltRouting,
|
||||
#endif
|
||||
};
|
||||
|
||||
std::string directive;
|
||||
std::string addr;
|
||||
std::string port;
|
||||
Protocol proto;
|
||||
SSLMode ssl = SSLUnspecified;
|
||||
unsigned int n_threads = 0;
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
ret += directive + ' ' + addr;
|
||||
if (!proto.is_local())
|
||||
ret += ' ' + port;
|
||||
ret += ' ' + std::string(proto.str()) + ' ' + openvpn::to_string(n_threads);
|
||||
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;
|
||||
}
|
||||
|
||||
Item port_offset(const unsigned int offset) const
|
||||
{
|
||||
Item ret(*this);
|
||||
if (ret.proto.is_unix()) // unix socket filenames should contain %s for "port" substitution
|
||||
ret.addr = printfmt(ret.addr, offset);
|
||||
else
|
||||
ret.port = openvpn::to_string(HostPort::parse_port(ret.port, "offset") + offset);
|
||||
ret.n_threads = 0;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class List : public std::vector<Item>
|
||||
{
|
||||
public:
|
||||
enum LoadMode {
|
||||
Nominal,
|
||||
AllowDefault,
|
||||
AllowEmpty
|
||||
};
|
||||
|
||||
List() {}
|
||||
|
||||
List(const Item& item)
|
||||
{
|
||||
push_back(item);
|
||||
}
|
||||
|
||||
List(Item&& item)
|
||||
{
|
||||
push_back(std::move(item));
|
||||
}
|
||||
|
||||
List(const OptionList& opt,
|
||||
const std::string& directive,
|
||||
const LoadMode load_mode,
|
||||
const unsigned int n_cores)
|
||||
{
|
||||
size_t n_listen = 0;
|
||||
|
||||
for (auto &o : opt)
|
||||
{
|
||||
if (match(directive, o))
|
||||
++n_listen;
|
||||
}
|
||||
|
||||
if (n_listen)
|
||||
{
|
||||
reserve(n_listen);
|
||||
|
||||
for (auto &o : opt)
|
||||
{
|
||||
if (match(directive, o))
|
||||
{
|
||||
o.touch();
|
||||
|
||||
unsigned int mult = 1;
|
||||
int local = 0;
|
||||
|
||||
Item e;
|
||||
|
||||
// directive
|
||||
e.directive = o.get(0, 64);
|
||||
|
||||
// IP address
|
||||
e.addr = o.get(1, 128);
|
||||
|
||||
// port number
|
||||
e.port = o.get(2, 16);
|
||||
if (Protocol::is_local_type(e.port))
|
||||
{
|
||||
local = 1;
|
||||
e.port = "";
|
||||
}
|
||||
else
|
||||
HostPort::validate_port(e.port, e.directive);
|
||||
|
||||
// protocol
|
||||
{
|
||||
const std::string title = e.directive + " protocol";
|
||||
e.proto = Protocol::parse(o.get(3-local, 16), Protocol::NO_SUFFIX, title.c_str());
|
||||
}
|
||||
if (!local)
|
||||
{
|
||||
// modify protocol based on IP version of given address
|
||||
const std::string title = e.directive + " addr";
|
||||
const IP::Addr addr = IP::Addr(e.addr, title.c_str());
|
||||
e.proto.mod_addr_version(addr);
|
||||
}
|
||||
|
||||
// number of threads
|
||||
int n_threads_exists = 0;
|
||||
{
|
||||
const std::string ntstr = o.get_optional(4-local, 16);
|
||||
if (ntstr.length() > 0 && string::is_digit(ntstr[0]))
|
||||
n_threads_exists = 1;
|
||||
}
|
||||
if (n_threads_exists)
|
||||
{
|
||||
std::string n_threads = o.get(4-local, 16);
|
||||
if (string::ends_with(n_threads, "*N"))
|
||||
{
|
||||
mult = n_cores;
|
||||
n_threads = n_threads.substr(0, n_threads.length() - 2);
|
||||
}
|
||||
if (!parse_number_validate<unsigned int>(n_threads, 3, 1, 100, &e.n_threads))
|
||||
OPENVPN_THROW(option_error, e.directive << ": bad num threads: " << n_threads);
|
||||
#ifndef OPENVPN_PLATFORM_WIN
|
||||
if (local && e.n_threads != 1)
|
||||
OPENVPN_THROW(option_error, e.directive << ": local socket only supports one thread per pathname (not " << n_threads << ')');
|
||||
#endif
|
||||
e.n_threads *= mult;
|
||||
}
|
||||
else
|
||||
e.n_threads = 1;
|
||||
|
||||
// SSL
|
||||
if (o.size() >= 5-local+n_threads_exists)
|
||||
{
|
||||
const std::string& ssl_qualifier = o.get(4-local+n_threads_exists, 16);
|
||||
if (ssl_qualifier == "ssl")
|
||||
{
|
||||
if (local)
|
||||
OPENVPN_THROW(option_error, e.directive << ": SSL not supported on local sockets");
|
||||
e.ssl = Item::SSLOn;
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
push_back(std::move(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (load_mode == AllowDefault)
|
||||
{
|
||||
Item e;
|
||||
|
||||
// parse "proto" option if present
|
||||
{
|
||||
const Option* o = opt.get_ptr("proto");
|
||||
if (o)
|
||||
e.proto = Protocol::parse(o->get(1, 16), Protocol::SERVER_SUFFIX);
|
||||
else
|
||||
e.proto = Protocol(Protocol::UDPv4);
|
||||
}
|
||||
|
||||
// parse "port" option if present
|
||||
{
|
||||
const Option* o = opt.get_ptr("lport");
|
||||
if (!o)
|
||||
o = opt.get_ptr("port");
|
||||
if (o)
|
||||
{
|
||||
e.port = o->get(1, 16);
|
||||
HostPort::validate_port(e.port, "listen");
|
||||
}
|
||||
else
|
||||
e.port = "1194";
|
||||
}
|
||||
|
||||
// parse "local" option if present
|
||||
{
|
||||
const Option* o = opt.get_ptr("local");
|
||||
if (o)
|
||||
{
|
||||
e.addr = o->get(1, 128);
|
||||
const IP::Addr addr = IP::Addr(e.addr, "local addr");
|
||||
e.proto.mod_addr_version(addr);
|
||||
}
|
||||
else if (e.proto.is_ipv6())
|
||||
e.addr = "::0";
|
||||
else
|
||||
e.addr = "0.0.0.0";
|
||||
}
|
||||
|
||||
// n_threads defaults to one unless "listen" directive is used
|
||||
e.n_threads = 1;
|
||||
|
||||
push_back(std::move(e));
|
||||
}
|
||||
else if (load_mode != AllowEmpty)
|
||||
OPENVPN_THROW(option_error, "no " << directive << " directives found");
|
||||
}
|
||||
|
||||
unsigned int total_threads() const
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
for (auto &i : *this)
|
||||
ret += i.n_threads;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string ret;
|
||||
for (auto &i : *this)
|
||||
{
|
||||
ret += i.to_string();
|
||||
ret += '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string local_addr() const
|
||||
{
|
||||
for (auto &i : *this)
|
||||
if (i.proto.is_local())
|
||||
return i.addr;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
List expand_ports_by_n_threads(const size_t max_size) const
|
||||
{
|
||||
List ret;
|
||||
for (const auto &e : *this)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
do {
|
||||
if (ret.size() >= max_size)
|
||||
OPENVPN_THROW(option_error, e.directive << ": max_size=" << max_size << " exceeded");
|
||||
ret.emplace_back(e.port_offset(offset));
|
||||
} while (++offset < e.n_threads);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
List expand_ports_by_unit(const unsigned int unit) const
|
||||
{
|
||||
List ret;
|
||||
for (const auto &e : *this)
|
||||
ret.emplace_back(e.port_offset(unit));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool match(const std::string& directive, const Option& o)
|
||||
{
|
||||
const size_t len = directive.length();
|
||||
if (len && o.size())
|
||||
{
|
||||
if (directive[len-1] == '-')
|
||||
return string::starts_with(o.ref(0), directive);
|
||||
else
|
||||
return o.ref(0) == directive;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Server-side client manager
|
||||
|
||||
#ifndef OPENVPN_SERVER_MANAGE_H
|
||||
#define OPENVPN_SERVER_MANAGE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/tun/server/tunbase.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/auth/authcreds.hpp>
|
||||
#include <openvpn/ssl/proto.hpp>
|
||||
#include <openvpn/server/servhalt.hpp>
|
||||
#include <openvpn/server/peerstats.hpp>
|
||||
#include <openvpn/server/peeraddr.hpp>
|
||||
#include <openvpn/auth/authcert.hpp>
|
||||
#include <openvpn/auth/authstatusconst.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace ManClientInstance {
|
||||
|
||||
// Base class for the per-client-instance state of the ManServer.
|
||||
// Each client instance uses this class to send data to the man layer.
|
||||
struct Send : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Send> Ptr;
|
||||
|
||||
virtual void pre_stop() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void auth_request(const AuthCreds::Ptr& auth_creds,
|
||||
const AuthCert::Ptr& auth_cert,
|
||||
const PeerAddr::Ptr& peer_addr) = 0;
|
||||
virtual void push_request(ProtoContext::Config::Ptr pconf) = 0;
|
||||
|
||||
// INFO notification
|
||||
virtual void info_request(const std::string& imsg) = 0;
|
||||
|
||||
// bandwidth stats notification
|
||||
virtual void stats_notify(const PeerStats& ps, const bool final) = 0;
|
||||
|
||||
// client float notification
|
||||
virtual void float_notify(const PeerAddr::Ptr& addr) = 0;
|
||||
|
||||
// ID
|
||||
virtual std::string instance_name() const = 0;
|
||||
virtual std::uint64_t instance_id() const = 0;
|
||||
|
||||
// return a JSON string describing connected user
|
||||
virtual std::string describe_user(const bool show_userprop) = 0;
|
||||
|
||||
// disconnect
|
||||
virtual void disconnect_user(const HaltRestart::Type type,
|
||||
const AuthStatus::Type auth_status,
|
||||
const std::string& reason,
|
||||
const bool tell_client) = 0;
|
||||
|
||||
// send control channel message
|
||||
virtual void post_info_user(BufferPtr&& info) = 0;
|
||||
|
||||
// set ACL index for user
|
||||
virtual void set_acl_index(const int acl_index,
|
||||
const std::string* username,
|
||||
const bool challenge) = 0;
|
||||
|
||||
// notify of local user properties update
|
||||
virtual void userprop_local_update() = 0;
|
||||
};
|
||||
|
||||
// Base class for the client instance receiver. Note that all
|
||||
// client instance receivers (transport, routing, management,
|
||||
// etc.) must inherit virtually from RC because the client instance
|
||||
// object will inherit from multiple receivers.
|
||||
struct Recv : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Recv> Ptr;
|
||||
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void auth_failed(const std::string& reason,
|
||||
const bool tell_client) = 0;
|
||||
|
||||
virtual void push_reply(std::vector<BufferPtr>&& push_msgs) = 0;
|
||||
|
||||
// push a halt or restart message to client
|
||||
virtual void push_halt_restart_msg(const HaltRestart::Type type,
|
||||
const std::string& reason,
|
||||
const bool tell_client) = 0;
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
// schedule an auth pending disconnect in seconds
|
||||
virtual void schedule_auth_pending_timeout(const unsigned int seconds) = 0;
|
||||
|
||||
// set up relay to target
|
||||
virtual void relay(const IP::Addr& target, const int port) = 0;
|
||||
|
||||
// get client bandwidth stats
|
||||
virtual PeerStats stats_poll() = 0;
|
||||
|
||||
// return true if management layer should preserve session ID
|
||||
virtual bool should_preserve_session_id() = 0;
|
||||
|
||||
// get native reference to client instance
|
||||
virtual TunClientInstance::NativeHandle tun_native_handle() = 0;
|
||||
};
|
||||
|
||||
struct Factory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Factory> Ptr;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual Send::Ptr new_obj(Recv* instance) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_SERVER_PEERADDR_H
|
||||
#define OPENVPN_SERVER_PEERADDR_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, uint64_t, etc.
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
struct AddrPort
|
||||
{
|
||||
AddrPort() : port(0) {}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return addr.to_string_bracket_ipv6() + ':' + openvpn::to_string(port);
|
||||
}
|
||||
|
||||
IP::Addr addr;
|
||||
std::uint16_t port;
|
||||
};
|
||||
|
||||
struct PeerAddr : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<PeerAddr> Ptr;
|
||||
|
||||
PeerAddr()
|
||||
: tcp(false)
|
||||
{
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string proto;
|
||||
if (tcp)
|
||||
proto = "TCP ";
|
||||
else
|
||||
proto = "UDP ";
|
||||
return proto + remote.to_string() + " -> " + local.to_string();
|
||||
}
|
||||
|
||||
AddrPort remote;
|
||||
AddrPort local;
|
||||
bool tcp;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_SERVER_PEERSTATS_H
|
||||
#define OPENVPN_SERVER_PEERSTATS_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, uint64_t, etc.
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
struct PeerStats
|
||||
{
|
||||
PeerStats()
|
||||
: rx_bytes(0),
|
||||
tx_bytes(0),
|
||||
status(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::uint64_t rx_bytes;
|
||||
std::uint64_t tx_bytes;
|
||||
int status;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_SERVER_SERVHALT_H
|
||||
#define OPENVPN_SERVER_SERVHALT_H
|
||||
|
||||
namespace openvpn {
|
||||
namespace HaltRestart {
|
||||
enum Type {
|
||||
HALT, // disconnect
|
||||
RESTART, // restart, don't preserve session token
|
||||
RESTART_PSID, // restart, preserve session token
|
||||
RESTART_PASSIVE, // restart, preserve session token and local client instance object
|
||||
AUTH_FAILED, // auth fail, don't preserve session token
|
||||
RAW, // pass raw message to client
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,752 @@
|
||||
// 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/>.
|
||||
|
||||
// OpenVPN protocol implementation for client-instance object on server
|
||||
|
||||
#ifndef OPENVPN_SERVER_SERVPROTO_H
|
||||
#define OPENVPN_SERVER_SERVPROTO_H
|
||||
|
||||
#include <memory>
|
||||
#include <utility> // for std::move
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/common/abort.hpp>
|
||||
#include <openvpn/common/link.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/buffer/bufstream.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/time/coarsetime.hpp>
|
||||
#include <openvpn/crypto/cryptodc.hpp>
|
||||
#include <openvpn/ssl/proto.hpp>
|
||||
#include <openvpn/transport/server/transbase.hpp>
|
||||
#include <openvpn/tun/server/tunbase.hpp>
|
||||
#include <openvpn/server/manage.hpp>
|
||||
|
||||
#ifdef OPENVPN_DEBUG_SERVPROTO
|
||||
#define OPENVPN_LOG_SERVPROTO(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_SERVPROTO(x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class ServerProto
|
||||
{
|
||||
typedef ProtoContext Base;
|
||||
typedef Link<TransportClientInstance::Send, TransportClientInstance::Recv> TransportLink;
|
||||
typedef Link<TunClientInstance::Send, TunClientInstance::Recv> TunLink;
|
||||
typedef Link<ManClientInstance::Send, ManClientInstance::Recv> ManLink;
|
||||
|
||||
public:
|
||||
class Session;
|
||||
|
||||
class Factory : public TransportClientInstance::Factory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Factory> Ptr;
|
||||
typedef Base::Config ProtoConfig;
|
||||
|
||||
Factory(openvpn_io::io_context& io_context_arg,
|
||||
const Base::Config& c)
|
||||
: io_context(io_context_arg)
|
||||
{
|
||||
if (c.tls_crypt_enabled())
|
||||
preval.reset(new Base::TLSCryptPreValidate(c, true));
|
||||
else if (c.tls_auth_enabled())
|
||||
preval.reset(new Base::TLSAuthPreValidate(c, true));
|
||||
}
|
||||
|
||||
virtual TransportClientInstance::Recv::Ptr new_client_instance() override;
|
||||
|
||||
virtual bool validate_initial_packet(const BufferAllocated& net_buf) override
|
||||
{
|
||||
if (preval)
|
||||
{
|
||||
const bool ret = preval->validate(net_buf);
|
||||
if (!ret)
|
||||
stats->error(Error::TLS_AUTH_FAIL);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoConfig::Ptr clone_proto_config() const
|
||||
{
|
||||
return new ProtoConfig(*proto_context_config);
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
ProtoConfig::Ptr proto_context_config;
|
||||
|
||||
ManClientInstance::Factory::Ptr man_factory;
|
||||
TunClientInstance::Factory::Ptr tun_factory;
|
||||
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
private:
|
||||
Base::TLSWrapPreValidate::Ptr preval;
|
||||
};
|
||||
|
||||
// This is the main server-side client instance object
|
||||
class Session : Base, // OpenVPN protocol implementation
|
||||
public TransportLink, // Transport layer
|
||||
public TunLink, // Tun/routing layer
|
||||
public ManLink // Management layer
|
||||
{
|
||||
friend class Factory; // calls constructor
|
||||
|
||||
typedef Base::PacketType PacketType;
|
||||
|
||||
using Base::now;
|
||||
using Base::stat;
|
||||
|
||||
public:
|
||||
typedef RCPtr<Session> Ptr;
|
||||
|
||||
virtual bool defined() const override
|
||||
{
|
||||
return defined_();
|
||||
}
|
||||
|
||||
virtual TunClientInstance::Recv* override_tun(TunClientInstance::Send* tun) override
|
||||
{
|
||||
TunLink::send.reset(tun);
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual void start(const TransportClientInstance::Send::Ptr& parent,
|
||||
const PeerAddr::Ptr& addr,
|
||||
const int local_peer_id) override
|
||||
{
|
||||
TransportLink::send = parent;
|
||||
peer_addr = addr;
|
||||
|
||||
// init OpenVPN protocol handshake
|
||||
Base::update_now();
|
||||
Base::reset();
|
||||
Base::set_local_peer_id(local_peer_id);
|
||||
Base::start();
|
||||
Base::flush(true);
|
||||
|
||||
// coarse wakeup range
|
||||
housekeeping_schedule.init(Time::Duration::binary_ms(512), Time::Duration::binary_ms(1024));
|
||||
}
|
||||
|
||||
virtual PeerStats stats_poll() override
|
||||
{
|
||||
if (TransportLink::send)
|
||||
return TransportLink::send->stats_poll();
|
||||
else
|
||||
return PeerStats();
|
||||
}
|
||||
|
||||
virtual bool should_preserve_session_id() override
|
||||
{
|
||||
return preserve_session_id;
|
||||
}
|
||||
|
||||
virtual void stop() override
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
housekeeping_timer.cancel();
|
||||
|
||||
if (ManLink::send)
|
||||
ManLink::send->pre_stop();
|
||||
|
||||
// deliver final peer stats to management layer
|
||||
if (TransportLink::send && ManLink::send)
|
||||
{
|
||||
if (TransportLink::send->stats_pending())
|
||||
ManLink::send->stats_notify(TransportLink::send->stats_poll(), true);
|
||||
}
|
||||
|
||||
Base::pre_destroy();
|
||||
Base::reset_dc_factory();
|
||||
if (TransportLink::send)
|
||||
{
|
||||
TransportLink::send->stop();
|
||||
TransportLink::send.reset();
|
||||
}
|
||||
if (TunLink::send)
|
||||
{
|
||||
TunLink::send->stop();
|
||||
TunLink::send.reset();
|
||||
}
|
||||
if (ManLink::send)
|
||||
{
|
||||
ManLink::send->stop();
|
||||
ManLink::send.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called with OpenVPN-encapsulated packets from transport layer
|
||||
virtual bool transport_recv(BufferAllocated& buf) override
|
||||
{
|
||||
bool ret = false;
|
||||
if (!Base::primary_defined())
|
||||
return false;
|
||||
try {
|
||||
OPENVPN_LOG_SERVPROTO("Transport RECV[" << buf.size() << "] " << client_endpoint_render() << ' ' << Base::dump_packet(buf));
|
||||
|
||||
// update current time
|
||||
Base::update_now();
|
||||
|
||||
// get packet type
|
||||
Base::PacketType pt = Base::packet_type(buf);
|
||||
|
||||
// process packet
|
||||
if (pt.is_data())
|
||||
{
|
||||
// data packet
|
||||
ret = Base::data_decrypt(pt, buf);
|
||||
if (buf.size())
|
||||
{
|
||||
#ifdef OPENVPN_PACKET_LOG
|
||||
log_packet(buf, false);
|
||||
#endif
|
||||
// make packet appear as incoming on tun interface
|
||||
if (true) // fixme: was tun
|
||||
{
|
||||
OPENVPN_LOG_SERVPROTO("TUN SEND[" << buf.size() << ']');
|
||||
// fixme -- code me
|
||||
}
|
||||
}
|
||||
|
||||
// do a lightweight flush
|
||||
Base::flush(false);
|
||||
}
|
||||
else if (pt.is_control())
|
||||
{
|
||||
// control packet
|
||||
ret = Base::control_net_recv(pt, std::move(buf));
|
||||
|
||||
// do a full flush
|
||||
Base::flush(true);
|
||||
}
|
||||
|
||||
// schedule housekeeping wakeup
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
error(e);
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// called with cleartext IP packets from routing layer
|
||||
virtual void tun_recv(BufferAllocated& buf) override
|
||||
{
|
||||
// fixme -- code me
|
||||
}
|
||||
|
||||
// Return true if keepalive parameter(s) are enabled.
|
||||
virtual bool is_keepalive_enabled() const override
|
||||
{
|
||||
return Base::is_keepalive_enabled();
|
||||
}
|
||||
|
||||
// Disable keepalive for rest of session, but fetch
|
||||
// the keepalive parameters (in seconds).
|
||||
virtual void disable_keepalive(unsigned int& keepalive_ping,
|
||||
unsigned int& keepalive_timeout) override
|
||||
{
|
||||
Base::disable_keepalive(keepalive_ping, keepalive_timeout);
|
||||
}
|
||||
|
||||
// override the data channel factory
|
||||
virtual void override_dc_factory(const CryptoDCFactory::Ptr& dc_factory) override
|
||||
{
|
||||
Base::dc_settings().set_factory(dc_factory);
|
||||
}
|
||||
|
||||
virtual ~Session()
|
||||
{
|
||||
// fatal error if destructor called while Session is active
|
||||
if (defined_())
|
||||
std::abort();
|
||||
}
|
||||
|
||||
private:
|
||||
Session(openvpn_io::io_context& io_context_arg,
|
||||
const Factory& factory,
|
||||
ManClientInstance::Factory::Ptr man_factory_arg,
|
||||
TunClientInstance::Factory::Ptr tun_factory_arg)
|
||||
: Base(factory.clone_proto_config(), factory.stats),
|
||||
io_context(io_context_arg),
|
||||
housekeeping_timer(io_context_arg),
|
||||
disconnect_at(Time::infinite()),
|
||||
stats(factory.stats),
|
||||
man_factory(man_factory_arg),
|
||||
tun_factory(tun_factory_arg)
|
||||
{}
|
||||
|
||||
bool defined_() const
|
||||
{
|
||||
return !halt && TransportLink::send;
|
||||
}
|
||||
|
||||
// proto base class calls here for control channel network sends
|
||||
virtual void control_net_send(const Buffer& net_buf) override
|
||||
{
|
||||
OPENVPN_LOG_SERVPROTO("Transport SEND[" << net_buf.size() << "] " << client_endpoint_render() << ' ' << Base::dump_packet(net_buf));
|
||||
if (TransportLink::send)
|
||||
{
|
||||
if (TransportLink::send->transport_send_const(net_buf))
|
||||
Base::update_last_sent();
|
||||
}
|
||||
}
|
||||
|
||||
// Called on server with credentials and peer info provided by client.
|
||||
// Should be overriden by derived class if credentials are required.
|
||||
virtual void server_auth(const std::string& username,
|
||||
const SafeString& password,
|
||||
const std::string& peer_info,
|
||||
const AuthCert::Ptr& auth_cert) override
|
||||
{
|
||||
constexpr size_t MAX_USERNAME_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_PASS_FMT),
|
||||
Unicode::utf8_printable(peer_info, Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT)));
|
||||
ManLink::send->auth_request(auth_creds, auth_cert, peer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
// proto base class calls here for app-level control-channel messages received
|
||||
virtual void control_recv(BufferPtr&& app_bp) override
|
||||
{
|
||||
const std::string msg = Unicode::utf8_printable(Base::template read_control_string<std::string>(*app_bp),
|
||||
Unicode::UTF8_FILTER);
|
||||
if (msg == "PUSH_REQUEST")
|
||||
{
|
||||
if (get_management())
|
||||
ManLink::send->push_request(Base::conf_ptr());
|
||||
else
|
||||
auth_failed("no management provider", false);
|
||||
}
|
||||
else if (string::starts_with(msg, "INFO,"))
|
||||
{
|
||||
if (get_management())
|
||||
ManLink::send->info_request(msg.substr(5));
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG("Unrecognized client request: " << msg);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void auth_failed(const std::string& reason,
|
||||
const bool tell_client) override
|
||||
{
|
||||
push_halt_restart_msg(HaltRestart::AUTH_FAILED, reason, tell_client);
|
||||
}
|
||||
|
||||
virtual void relay(const IP::Addr& target, const int port) override
|
||||
{
|
||||
if (halt || disconnect_type == DT_HALT_RESTART)
|
||||
return;
|
||||
|
||||
Base::update_now();
|
||||
|
||||
if (TunLink::send && (disconnect_type < DT_RELAY_TRANSITION))
|
||||
{
|
||||
disconnect_type = DT_RELAY_TRANSITION;
|
||||
TunLink::send->relay(target, port);
|
||||
disconnect_in(Time::Duration::seconds(10)); // not a real disconnect, just complete transition to relay
|
||||
}
|
||||
|
||||
if (Base::primary_defined())
|
||||
{
|
||||
BufferPtr buf(new BufferAllocated(64, 0));
|
||||
buf_append_string(*buf, "RELAY");
|
||||
buf->null_terminate();
|
||||
Base::control_send(std::move(buf));
|
||||
Base::flush(true);
|
||||
}
|
||||
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void push_reply(std::vector<BufferPtr>&& push_msgs) override
|
||||
{
|
||||
if (halt || (disconnect_type >= DT_RELAY_TRANSITION) || !Base::primary_defined())
|
||||
return;
|
||||
|
||||
if (disconnect_type == DT_AUTH_PENDING)
|
||||
{
|
||||
disconnect_type = DT_NONE;
|
||||
cancel_disconnect();
|
||||
}
|
||||
|
||||
Base::update_now();
|
||||
|
||||
if (get_tun())
|
||||
{
|
||||
Base::init_data_channel();
|
||||
for (auto &msg : push_msgs)
|
||||
{
|
||||
msg->null_terminate();
|
||||
Base::control_send(std::move(msg));
|
||||
}
|
||||
Base::flush(true);
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
else
|
||||
{
|
||||
auth_failed("no tun provider", false);
|
||||
}
|
||||
}
|
||||
|
||||
virtual TunClientInstance::NativeHandle tun_native_handle() override
|
||||
{
|
||||
if (get_tun())
|
||||
return TunLink::send->tun_native_handle();
|
||||
else
|
||||
return TunClientInstance::NativeHandle();
|
||||
}
|
||||
|
||||
virtual void push_halt_restart_msg(const HaltRestart::Type type,
|
||||
const std::string& reason,
|
||||
const bool tell_client) override
|
||||
{
|
||||
if (halt || disconnect_type == DT_HALT_RESTART)
|
||||
return;
|
||||
|
||||
Base::update_now();
|
||||
|
||||
BufferPtr buf(new BufferAllocated(128, BufferAllocated::GROW));
|
||||
BufferStreamOut os(*buf);
|
||||
|
||||
std::string ts;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case HaltRestart::HALT:
|
||||
ts = "HALT";
|
||||
os << "HALT,";
|
||||
if (tell_client && !reason.empty())
|
||||
os << reason;
|
||||
else
|
||||
os << "client was disconnected from server";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RESTART:
|
||||
ts = "RESTART";
|
||||
os << "RESTART,";
|
||||
if (tell_client && !reason.empty())
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RESTART_PASSIVE:
|
||||
ts = "RESTART_PASSIVE";
|
||||
os << "RESTART,[P]:";
|
||||
if (tell_client && !reason.empty())
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
break;
|
||||
case HaltRestart::RESTART_PSID:
|
||||
ts = "RESTART_PSID";
|
||||
os << "RESTART,[P]:";
|
||||
if (tell_client && !reason.empty())
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
break;
|
||||
case HaltRestart::AUTH_FAILED:
|
||||
ts = "AUTH_FAILED";
|
||||
os << ts;
|
||||
if (tell_client && !reason.empty())
|
||||
os << ',' << reason;
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RAW:
|
||||
{
|
||||
const size_t pos = reason.find_first_of(',');
|
||||
if (pos != std::string::npos)
|
||||
ts = reason.substr(0, pos);
|
||||
else
|
||||
ts = reason;
|
||||
os << reason;
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_LOG("Disconnect: " << ts << ' ' << reason);
|
||||
|
||||
if (Base::primary_defined())
|
||||
{
|
||||
buf->null_terminate();
|
||||
Base::control_send(std::move(buf));
|
||||
Base::flush(true);
|
||||
}
|
||||
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void schedule_disconnect(const unsigned int seconds)
|
||||
{
|
||||
if (halt || disconnect_type == DT_HALT_RESTART)
|
||||
return;
|
||||
Base::update_now();
|
||||
disconnect_in(Time::Duration::seconds(seconds));
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void schedule_auth_pending_timeout(const unsigned int seconds)
|
||||
{
|
||||
if (halt || (disconnect_type >= DT_RELAY_TRANSITION) || !seconds)
|
||||
return;
|
||||
Base::update_now();
|
||||
disconnect_type = DT_AUTH_PENDING;
|
||||
disconnect_in(Time::Duration::seconds(seconds));
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void post_cc_msg(BufferPtr&& msg) override
|
||||
{
|
||||
if (halt || !Base::primary_defined())
|
||||
return;
|
||||
|
||||
Base::update_now();
|
||||
msg->null_terminate();
|
||||
Base::control_send(std::move(msg));
|
||||
Base::flush(true);
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void stats_notify(const PeerStats& ps, const bool final) override
|
||||
{
|
||||
if (ManLink::send)
|
||||
ManLink::send->stats_notify(ps, final);
|
||||
}
|
||||
|
||||
virtual void float_notify(const PeerAddr::Ptr& addr) override
|
||||
{
|
||||
if (ManLink::send)
|
||||
ManLink::send->float_notify(addr);
|
||||
}
|
||||
|
||||
virtual void data_limit_notify(const int key_id,
|
||||
const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status) override
|
||||
{
|
||||
Base::update_now();
|
||||
Base::data_limit_notify(key_id, cdl_mode, cdl_status);
|
||||
Base::flush(true);
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
bool get_management()
|
||||
{
|
||||
if (!ManLink::send)
|
||||
{
|
||||
if (man_factory)
|
||||
ManLink::send = man_factory->new_obj(this);
|
||||
}
|
||||
return bool(ManLink::send);
|
||||
}
|
||||
|
||||
bool get_tun()
|
||||
{
|
||||
if (!TunLink::send)
|
||||
{
|
||||
if (tun_factory)
|
||||
TunLink::send = tun_factory->new_obj(this);
|
||||
}
|
||||
return bool(TunLink::send);
|
||||
}
|
||||
|
||||
// caller must ensure that update_now() was called before
|
||||
// and set_housekeeping_timer() called after this method
|
||||
void disconnect_in(const Time::Duration& dur)
|
||||
{
|
||||
disconnect_at = now() + dur;
|
||||
}
|
||||
|
||||
void cancel_disconnect()
|
||||
{
|
||||
disconnect_at = Time::infinite();
|
||||
}
|
||||
|
||||
void housekeeping_callback(const openvpn_io::error_code& e)
|
||||
{
|
||||
try {
|
||||
if (!e && !halt)
|
||||
{
|
||||
// update current time
|
||||
Base::update_now();
|
||||
|
||||
housekeeping_schedule.reset();
|
||||
Base::housekeeping();
|
||||
if (Base::invalidated())
|
||||
invalidation_error(Base::invalidation_reason());
|
||||
else if (now() >= disconnect_at)
|
||||
{
|
||||
switch (disconnect_type)
|
||||
{
|
||||
case DT_HALT_RESTART:
|
||||
error("disconnect triggered");
|
||||
break;
|
||||
case DT_RELAY_TRANSITION:
|
||||
Base::pre_destroy();
|
||||
break;
|
||||
case DT_AUTH_PENDING:
|
||||
auth_failed("Auth Pending Timeout", true);
|
||||
break;
|
||||
default:
|
||||
error("unknown disconnect");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
|
||||
void set_housekeeping_timer()
|
||||
{
|
||||
Time next = Base::next_housekeeping();
|
||||
next.min(disconnect_at);
|
||||
if (!housekeeping_schedule.similar(next))
|
||||
{
|
||||
if (!next.is_infinite())
|
||||
{
|
||||
next.max(now());
|
||||
housekeeping_schedule.reset(next);
|
||||
housekeeping_timer.expires_at(next);
|
||||
housekeeping_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
self->housekeeping_callback(error);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
housekeeping_timer.cancel();
|
||||
housekeeping_schedule.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string client_endpoint_render()
|
||||
{
|
||||
if (TransportLink::send)
|
||||
return TransportLink::send->transport_info();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void error(const std::string& error)
|
||||
{
|
||||
OPENVPN_LOG("ServerProto: " << error);
|
||||
stop();
|
||||
}
|
||||
|
||||
void error(const std::exception& e)
|
||||
{
|
||||
error(e.what());
|
||||
}
|
||||
|
||||
void error()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void invalidation_error(const Error::Type err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case Error::KEV_NEGOTIATE_ERROR:
|
||||
case Error::KEEPALIVE_TIMEOUT:
|
||||
error();
|
||||
break;
|
||||
default:
|
||||
error(std::string("Session invalidated: ") + Error::name(err));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
|
||||
// higher values are higher priority
|
||||
enum DisconnectType {
|
||||
DT_NONE=0,
|
||||
DT_AUTH_PENDING,
|
||||
DT_RELAY_TRANSITION,
|
||||
DT_HALT_RESTART,
|
||||
};
|
||||
int disconnect_type = DT_NONE;
|
||||
bool preserve_session_id = true;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
PeerAddr::Ptr peer_addr;
|
||||
|
||||
CoarseTime housekeeping_schedule;
|
||||
AsioTimer housekeeping_timer;
|
||||
|
||||
Time disconnect_at;
|
||||
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
ManClientInstance::Factory::Ptr man_factory;
|
||||
TunClientInstance::Factory::Ptr tun_factory;
|
||||
};
|
||||
};
|
||||
|
||||
inline TransportClientInstance::Recv::Ptr ServerProto::Factory::new_client_instance()
|
||||
{
|
||||
return new Session(io_context, *this, man_factory, tun_factory);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,223 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_SERVER_VPNSERVNETBLOCK_H
|
||||
#define OPENVPN_SERVER_VPNSERVNETBLOCK_H
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/addr/range.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class VPNServerNetblock
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(vpn_serv_netblock);
|
||||
|
||||
struct Netblock
|
||||
{
|
||||
Netblock() {}
|
||||
|
||||
Netblock(const IP::Route& route)
|
||||
{
|
||||
if (!route.is_canonical())
|
||||
throw vpn_serv_netblock("not canonical");
|
||||
if (route.host_bits() < 2)
|
||||
throw vpn_serv_netblock("need at least 4 addresses in netblock");
|
||||
net = route.addr;
|
||||
server_gw = net + 1;
|
||||
prefix_len = route.prefix_len;
|
||||
}
|
||||
|
||||
bool defined() const { return net.defined(); }
|
||||
|
||||
IP::Addr netmask() const
|
||||
{
|
||||
return IP::Addr::netmask_from_prefix_len(net.version(), prefix_len);
|
||||
}
|
||||
|
||||
bool contains(const IP::Addr& a) const
|
||||
{
|
||||
if (net.defined() && net.version() == a.version())
|
||||
return (a & netmask()) == net;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return '[' + net.to_string() + '/' + std::to_string(prefix_len) + ',' + server_gw.to_string() + ']';
|
||||
}
|
||||
|
||||
IP::Addr net;
|
||||
IP::Addr server_gw;
|
||||
unsigned int prefix_len = 0;
|
||||
};
|
||||
|
||||
struct ClientNetblock : public Netblock
|
||||
{
|
||||
ClientNetblock() {}
|
||||
|
||||
ClientNetblock(const IP::Route& route)
|
||||
: Netblock(route)
|
||||
{
|
||||
const size_t extent = route.extent();
|
||||
bcast = net + (extent - 1);
|
||||
clients = IP::Range(net + 2, extent - 3);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return '[' + Netblock::to_string() + ','
|
||||
+ clients.to_string() + ','
|
||||
+ bcast.to_string() + ']';
|
||||
}
|
||||
|
||||
IP::Range clients;
|
||||
IP::Addr bcast;
|
||||
};
|
||||
|
||||
class PerThread
|
||||
{
|
||||
friend class VPNServerNetblock;
|
||||
|
||||
public:
|
||||
const IP::Range& range4() const { return range4_; }
|
||||
|
||||
bool range6_defined() const { return range6_.defined(); }
|
||||
const IP::Range& range6() const { return range6_; }
|
||||
|
||||
private:
|
||||
IP::Range range4_;
|
||||
IP::Range range6_;
|
||||
};
|
||||
|
||||
VPNServerNetblock(const OptionList& opt,
|
||||
const std::string& opt_name,
|
||||
const bool ipv4_optional,
|
||||
const unsigned int n_threads)
|
||||
{
|
||||
// ifconfig
|
||||
if (!ipv4_optional || opt.exists(opt_name))
|
||||
{
|
||||
const Option& o = opt.get(opt_name);
|
||||
const IP::Addr gw(o.get(1, 64), opt_name + " gateway");
|
||||
const IP::Addr nm(o.get(2, 64), opt_name + " netmask");
|
||||
IP::Route rt(gw, nm.prefix_len());
|
||||
if (rt.version() != IP::Addr::V4)
|
||||
throw vpn_serv_netblock(opt_name + " address is not IPv4");
|
||||
rt.force_canonical();
|
||||
snb4 = ClientNetblock(rt);
|
||||
if (snb4.server_gw != gw)
|
||||
throw vpn_serv_netblock(opt_name + " local gateway must be first usable address of subnet");
|
||||
}
|
||||
|
||||
// ifconfig-ipv6
|
||||
{
|
||||
const Option* o = opt.get_ptr(opt_name + "-ipv6");
|
||||
if (o)
|
||||
{
|
||||
IP::Route rt(o->get(1, 64), opt_name + "-ipv6 network");
|
||||
if (rt.version() != IP::Addr::V6)
|
||||
throw vpn_serv_netblock(opt_name + "-ipv6 network is not IPv6");
|
||||
if (!rt.is_canonical())
|
||||
throw vpn_serv_netblock(opt_name + "-ipv6 network is not canonical");
|
||||
snb6 = ClientNetblock(rt);
|
||||
}
|
||||
}
|
||||
|
||||
if (n_threads)
|
||||
{
|
||||
// IPv4 per-thread partition
|
||||
{
|
||||
IP::RangePartition rp(snb4.clients, n_threads);
|
||||
IP::Range crange;
|
||||
for (unsigned int i = 0; i < n_threads; ++i)
|
||||
{
|
||||
if (!rp.next(crange))
|
||||
throw vpn_serv_netblock(opt_name + " : unexpected ServerNetblock4 partition fail");
|
||||
PerThread pt;
|
||||
pt.range4_ = crange;
|
||||
thr.push_back(pt);
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6 per-thread partition
|
||||
if (snb6.defined())
|
||||
{
|
||||
IP::RangePartition rp(snb6.clients, n_threads);
|
||||
IP::Range crange;
|
||||
for (unsigned int i = 0; i < n_threads; ++i)
|
||||
{
|
||||
if (!rp.next(crange))
|
||||
throw vpn_serv_netblock(opt_name + " : unexpected ServerNetblock6 partition fail");
|
||||
thr[i].range6_ = crange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ClientNetblock& netblock4() const { return snb4; }
|
||||
const ClientNetblock& netblock6() const { return snb6; }
|
||||
|
||||
bool netblock_contains(const IP::Addr& a) const
|
||||
{
|
||||
return snb4.contains(a) || snb6.contains(a);
|
||||
}
|
||||
|
||||
const size_t size() const { return thr.size(); }
|
||||
|
||||
const PerThread& per_thread(const size_t index) const
|
||||
{
|
||||
return thr[index];
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "IPv4: " << snb4.to_string() << std::endl;
|
||||
if (snb6.defined())
|
||||
os << "IPv6: " << snb6.to_string() << std::endl;
|
||||
for (size_t i = 0; i < thr.size(); ++i)
|
||||
{
|
||||
const PerThread& pt = thr[i];
|
||||
os << '[' << i << ']';
|
||||
os << " v4=" << pt.range4().to_string();
|
||||
if (pt.range6_defined())
|
||||
os << " v6=" << pt.range6().to_string();
|
||||
os << std::endl;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
private:
|
||||
ClientNetblock snb4;
|
||||
ClientNetblock snb6;
|
||||
std::vector<PerThread> thr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_SERVER_VPNSERVPOOL_H
|
||||
#define OPENVPN_SERVER_VPNSERVPOOL_H
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <cstdint> // for std::uint32_t
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/arraysize.hpp>
|
||||
#include <openvpn/server/vpnservnetblock.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/addr/pool.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace VPNServerPool {
|
||||
|
||||
OPENVPN_EXCEPTION(vpn_serv_pool_error);
|
||||
|
||||
struct IP46
|
||||
{
|
||||
void add_routes(std::vector<IP::Route>& rtvec)
|
||||
{
|
||||
if (ip4.defined())
|
||||
rtvec.emplace_back(ip4, ip4.size());
|
||||
if (ip6.defined())
|
||||
rtvec.emplace_back(ip6, ip6.size());
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << '[' << ip4 << ' ' << ip6 << ']';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool defined() const
|
||||
{
|
||||
return ip4.defined() || ip6.defined();
|
||||
}
|
||||
|
||||
IP::Addr ip4;
|
||||
IP::Addr ip6;
|
||||
};
|
||||
|
||||
class Pool : public VPNServerNetblock
|
||||
{
|
||||
public:
|
||||
enum Flags {
|
||||
IPv4_DEPLETION=(1<<0),
|
||||
IPv6_DEPLETION=(1<<1),
|
||||
};
|
||||
|
||||
Pool(const OptionList& opt)
|
||||
: VPNServerNetblock(init_snb_from_opt(opt))
|
||||
{
|
||||
if (configured(opt, "server"))
|
||||
{
|
||||
pool4.add_range(netblock4().clients);
|
||||
pool6.add_range(netblock6().clients);
|
||||
}
|
||||
}
|
||||
|
||||
// returns flags
|
||||
unsigned int acquire(IP46& addr_pair, const bool request_ipv6)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
unsigned int flags = 0;
|
||||
if (!pool4.acquire_addr(addr_pair.ip4))
|
||||
flags |= IPv4_DEPLETION;
|
||||
if (request_ipv6 && netblock6().defined())
|
||||
{
|
||||
if (!pool6.acquire_addr(addr_pair.ip6))
|
||||
flags |= IPv6_DEPLETION;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
void release(IP46& addr_pair)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (addr_pair.ip4.defined())
|
||||
pool4.release_addr(addr_pair.ip4);
|
||||
if (addr_pair.ip6.defined())
|
||||
pool6.release_addr(addr_pair.ip6);
|
||||
}
|
||||
|
||||
private:
|
||||
static VPNServerNetblock init_snb_from_opt(const OptionList& opt)
|
||||
{
|
||||
if (configured(opt, "server"))
|
||||
return VPNServerNetblock(opt, "server", false, 0);
|
||||
else if (configured(opt, "ifconfig"))
|
||||
return VPNServerNetblock(opt, "ifconfig", false, 0);
|
||||
else
|
||||
throw vpn_serv_pool_error("one of 'server' or 'ifconfig' directives is required");
|
||||
}
|
||||
|
||||
static bool configured(const OptionList& opt,
|
||||
const std::string& opt_name)
|
||||
{
|
||||
return opt.exists(opt_name) || opt.exists(opt_name + "-ipv6");
|
||||
}
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
IP::Pool pool4;
|
||||
IP::Pool pool6;
|
||||
};
|
||||
|
||||
class IP46AutoRelease : public IP46, public RC<thread_safe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<IP46AutoRelease> Ptr;
|
||||
|
||||
IP46AutoRelease(Pool* pool_arg)
|
||||
: pool(pool_arg)
|
||||
{
|
||||
}
|
||||
|
||||
~IP46AutoRelease()
|
||||
{
|
||||
if (pool)
|
||||
pool->release(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
Pool* pool;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user