Squashed 'OpenVPN Adapter/Vendors/openvpn/' content from commit da99df6

git-subtree-dir: OpenVPN Adapter/Vendors/openvpn
git-subtree-split: da99df69492256d7a18bbea303ae98457782a4bf
This commit is contained in:
Sergey Abramchuk
2017-04-09 14:13:07 +03:00
commit f65d76170b
519 changed files with 88163 additions and 0 deletions
+281
View File
@@ -0,0 +1,281 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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/to_string.hpp>
#include <openvpn/addr/ip.hpp>
#include <openvpn/transport/protocol.hpp>
namespace openvpn {
namespace Listen {
struct Item
{
enum SSLMode {
SSLUnspecified,
SSLOn,
SSLOff,
};
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::ostringstream os;
os << directive << ' ' << addr;
if (!proto.is_local())
os << ' ' << port;
os << ' ' << proto.str() << ' ' << n_threads;
if (ssl == SSLOn)
os << " ssl";
else if (ssl == SSLOff)
os << " !ssl";
return os.str();
}
Item port_offset(const unsigned int offset) const
{
Item ret(*this);
ret.port = openvpn::to_string(HostPort::parse_port(ret.port, "offset") + offset);
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 (OptionList::const_iterator i = opt.begin(); i != opt.end(); ++i)
{
const Option& o = *i;
if (match(directive, o))
++n_listen;
}
if (n_listen)
{
reserve(n_listen);
for (OptionList::const_iterator i = opt.begin(); i != opt.end(); ++i)
{
const Option& o = *i;
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;
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 (const_iterator i = begin(); i != end(); ++i)
ret += i->n_threads;
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
+136
View File
@@ -0,0 +1,136 @@
// 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/>.
// 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>
namespace openvpn {
// 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 ManClientInstanceSend : public virtual RC<thread_unsafe_refcount>
{
typedef RCPtr<ManClientInstanceSend> Ptr;
//virtual bool defined() const = 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(const 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() = 0;
// disconnect
virtual void disconnect_user(const HaltRestart::Type type,
const std::string& reason,
const bool tell_client) = 0;
// send control channel message
virtual void post_info_user(BufferPtr&& info) = 0;
// set ACL ID for user
virtual void set_acl_id(const int acl_id,
const std::string* username,
const bool challenge,
const bool throw_on_error) = 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 ManClientInstanceRecv : public virtual RC<thread_unsafe_refcount>
{
typedef RCPtr<ManClientInstanceRecv> Ptr;
//virtual bool defined() const = 0;
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,
const std::vector<IP::Route>& routes,
const unsigned int initial_fwmark) = 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;
// set fwmark value in client instance
virtual void set_fwmark(const unsigned int fwmark) = 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;
};
struct ManClientInstanceFactory : public RC<thread_unsafe_refcount>
{
typedef RCPtr<ManClientInstanceFactory> Ptr;
virtual void start() = 0;
virtual ManClientInstanceSend::Ptr new_obj(ManClientInstanceRecv* instance) = 0;
};
}
#endif
+70
View File
@@ -0,0 +1,70 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+45
View File
@@ -0,0 +1,45 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+38
View File
@@ -0,0 +1,38 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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
+691
View File
@@ -0,0 +1,691 @@
// 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/>.
// 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<TransportClientInstanceSend, TransportClientInstanceRecv> TransportLink;
typedef Link<TunClientInstanceSend, TunClientInstanceRecv> TunLink;
typedef Link<ManClientInstanceSend, ManClientInstanceRecv> ManLink;
public:
class Session;
class Factory : public TransportClientInstanceFactory
{
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_auth_enabled())
preval.reset(new Base::TLSAuthPreValidate(c, true));
}
virtual TransportClientInstanceRecv::Ptr new_client_instance();
virtual bool validate_initial_packet(const Buffer& net_buf)
{
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;
ManClientInstanceFactory::Ptr man_factory;
TunClientInstanceFactory::Ptr tun_factory;
SessionStats::Ptr stats;
private:
Base::TLSAuthPreValidate::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
{
return defined_();
}
virtual TunClientInstanceRecv* override_tun(TunClientInstanceSend* tun)
{
TunLink::send.reset(tun);
return this;
}
virtual void start(const TransportClientInstanceSend::Ptr& parent,
const PeerAddr::Ptr& addr,
const int local_peer_id)
{
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()
{
if (TransportLink::send)
return TransportLink::send->stats_poll();
else
return PeerStats();
}
virtual void stop()
{
if (!halt)
{
halt = true;
housekeeping_timer.cancel();
// 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)
{
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)
{
// fixme -- code me
}
// Return true if keepalive parameter(s) are enabled.
virtual bool is_keepalive_enabled() const
{
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)
{
Base::disable_keepalive(keepalive_ping, keepalive_timeout);
}
// override the data channel factory
virtual void override_dc_factory(const CryptoDCFactory::Ptr& dc_factory)
{
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,
ManClientInstanceFactory::Ptr man_factory_arg,
TunClientInstanceFactory::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)
{
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)
{
constexpr size_t MAX_USERNAME_SIZE = 256;
constexpr size_t MAX_PASSWORD_SIZE = 256;
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(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)
{
const std::string msg = Unicode::utf8_printable(Base::template read_control_string<std::string>(*app_bp),
Unicode::UTF8_FILTER);
if (msg == "PUSH_REQUEST")
{
if (!did_push)
{
did_push = true;
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)
{
push_halt_restart_msg(HaltRestart::AUTH_FAILED, reason, tell_client);
}
virtual void set_fwmark(const unsigned int fwmark)
{
if (TunLink::send)
TunLink::send->set_fwmark(fwmark);
}
virtual void relay(const IP::Addr& target, const int port)
{
Base::update_now();
if (TunLink::send && !relay_transition)
{
relay_transition = true;
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,
const std::vector<IP::Route>& rtvec,
const unsigned int initial_fwmark)
{
if (halt || relay_transition || !Base::primary_defined())
return;
Base::update_now();
if (get_tun())
{
Base::init_data_channel();
if (initial_fwmark)
TunLink::send->set_fwmark(initial_fwmark);
TunLink::send->add_routes(rtvec);
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 void push_halt_restart_msg(const HaltRestart::Type type,
const std::string& reason,
const bool tell_client)
{
if (halt || did_client_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";
break;
case HaltRestart::RESTART:
ts = "RESTART";
os << "RESTART,";
if (tell_client && !reason.empty())
os << reason;
else
os << "server requested a client reconnect";
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";
break;
case HaltRestart::AUTH_FAILED:
ts = "AUTH_FAILED";
os << ts;
if (tell_client && !reason.empty())
os << ',' << reason;
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;
break;
}
}
OPENVPN_LOG("Disconnect: " << ts << ' ' << reason);
if (type != HaltRestart::RESTART_PASSIVE)
{
did_client_halt_restart = true;
disconnect_in(Time::Duration::seconds(1));
}
if (Base::primary_defined())
{
buf->null_terminate();
Base::control_send(std::move(buf));
Base::flush(true);
}
set_housekeeping_timer();
}
virtual void post_cc_msg(BufferPtr&& msg)
{
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)
{
if (ManLink::send)
ManLink::send->stats_notify(ps, final);
}
virtual void float_notify(const PeerAddr::Ptr& addr)
{
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)
{
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 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)
{
if (relay_transition && !did_client_halt_restart)
Base::pre_destroy();
else
error("disconnect triggered");
}
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();
}
}
}
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;
bool halt = false;
bool did_push = false;
bool did_client_halt_restart = false;
bool relay_transition = false;
PeerAddr::Ptr peer_addr;
CoarseTime housekeeping_schedule;
AsioTimer housekeeping_timer;
Time disconnect_at;
SessionStats::Ptr stats;
ManClientInstanceFactory::Ptr man_factory;
TunClientInstanceFactory::Ptr tun_factory;
};
};
inline TransportClientInstanceRecv::Ptr ServerProto::Factory::new_client_instance()
{
return new Session(io_context, *this, man_factory, tun_factory);
}
}
#endif
+208
View File
@@ -0,0 +1,208 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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() : prefix_len(0) {}
Netblock(const IP::Route& route)
{
if (!route.is_canonical())
throw vpn_serv_netblock("not canonical");
const size_t extent = route.extent();
if (extent < 4)
throw vpn_serv_netblock("need at least 4 addresses in netblock");
net = route.addr;
server_gw = net + 1;
bcast = net + (extent - 1);
clients = IP::Range(net + 2, extent - 3);
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() + ','
+ server_gw.to_string() + ','
+ clients.to_string() + ','
+ bcast.to_string() + ']';
}
IP::Addr net;
IP::Addr server_gw;
IP::Range clients;
IP::Addr bcast;
unsigned int prefix_len;
};
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 = Netblock(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 = Netblock(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 Netblock& netblock4() const { return snb4; }
const Netblock& 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:
Netblock snb4;
Netblock snb6;
std::vector<PerThread> thr;
};
}
#endif
+159
View File
@@ -0,0 +1,159 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_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