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,77 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_ALTPROXY_H
|
||||
#define OPENVPN_TRANSPORT_ALTPROXY_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/transport/socket_protect.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
#include <openvpn/crypto/digestapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
struct AltProxy : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
struct Config
|
||||
{
|
||||
Config()
|
||||
: free_list_max_size(8),
|
||||
socket_protect(nullptr)
|
||||
{}
|
||||
|
||||
RemoteList::Ptr remote_list;
|
||||
size_t free_list_max_size;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
RandomAPI::Ptr rng;
|
||||
DigestFactory::Ptr digest_factory;
|
||||
|
||||
SocketProtect* socket_protect;
|
||||
};
|
||||
|
||||
typedef RCPtr<AltProxy> Ptr;
|
||||
|
||||
// return proxy name
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
// called to indicate whether or not remote_list should be cached
|
||||
virtual void set_enable_cache(const bool enable_cache) = 0;
|
||||
|
||||
// return a RemoteList::Ptr (optional) to precache it
|
||||
virtual void precache(RemoteList::Ptr& r) = 0;
|
||||
|
||||
// iterate to next host in proxy-specific remote_list, return true
|
||||
// to prevent next() from being called on global remote_list
|
||||
virtual bool next() = 0;
|
||||
|
||||
// return true if this proxy method only supports TCP transport
|
||||
virtual bool requires_tcp() const = 0;
|
||||
|
||||
// return a new TransportClientFactory for this proxy
|
||||
virtual TransportClientFactory::Ptr new_transport_client_factory(const Config&) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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_TRANSPORT_CLIENT_EXTERN_CONFIG_H
|
||||
#define OPENVPN_TRANSPORT_CLIENT_EXTERN_CONFIG_H
|
||||
|
||||
#include <sstream>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/transport/socket_protect.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
|
||||
namespace openvpn
|
||||
{
|
||||
namespace ExternalTransport
|
||||
{
|
||||
struct Config
|
||||
{
|
||||
Protocol protocol;
|
||||
RemoteList::Ptr remote_list;
|
||||
bool server_addr_float = false;
|
||||
bool synchronous_dns_lookup = false;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
SocketProtect* socket_protect = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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_TRANSPORT_CLIENT_EXTERN_FW_H
|
||||
#define OPENVPN_TRANSPORT_CLIENT_EXTERN_FW_H
|
||||
|
||||
#ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace ExternalTransport {
|
||||
#ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY
|
||||
struct Config;
|
||||
struct Factory
|
||||
{
|
||||
virtual TransportClientFactory* new_transport_factory(const Config& conf) = 0;
|
||||
virtual ~Factory() {}
|
||||
};
|
||||
#else
|
||||
struct Factory {};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Create a special transport factory that persists an existing
|
||||
// transport client. This is used to preserve the transport
|
||||
// socket when other client components are restarted after
|
||||
// a RELAY message is received from the server.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_CLIENT_RELAY_H
|
||||
#define OPENVPN_TRANSPORT_CLIENT_RELAY_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class TransportRelayFactory : public TransportClientFactory
|
||||
{
|
||||
public:
|
||||
TransportRelayFactory(openvpn_io::io_context& io_context,
|
||||
TransportClient::Ptr transport,
|
||||
TransportClientParent* old_parent)
|
||||
: io_context_(io_context),
|
||||
transport_(std::move(transport)),
|
||||
null_parent_(new NullParent(old_parent))
|
||||
{
|
||||
// Temporarily point transport to our null parent
|
||||
transport_->transport_reparent(null_parent_.get());
|
||||
}
|
||||
|
||||
class TransportClientNull : public TransportClient
|
||||
{
|
||||
public:
|
||||
TransportClientNull(TransportClient* old)
|
||||
: endpoint_(old->server_endpoint_addr()),
|
||||
protocol_(old->transport_protocol())
|
||||
{
|
||||
old->server_endpoint_info(host_, port_, proto_, ip_addr_);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void transport_start() {}
|
||||
virtual void stop() {}
|
||||
virtual bool transport_send_const(const Buffer& buf) { return false; }
|
||||
virtual bool transport_send(BufferAllocated& buf) { return false; }
|
||||
virtual bool transport_send_queue_empty() { return false; }
|
||||
virtual bool transport_has_send_queue() { return false; }
|
||||
virtual unsigned int transport_send_queue_size() { return 0; }
|
||||
virtual void transport_stop_requeueing() { }
|
||||
virtual void reset_align_adjust(const size_t align_adjust) {}
|
||||
virtual void transport_reparent(TransportClientParent* parent) {}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
{
|
||||
return endpoint_;
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
{
|
||||
return protocol_;
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
{
|
||||
host = host_;
|
||||
port = port_;
|
||||
proto = proto_;
|
||||
ip_addr = ip_addr_;
|
||||
}
|
||||
|
||||
IP::Addr endpoint_;
|
||||
Protocol protocol_;
|
||||
std::string host_;
|
||||
std::string port_;
|
||||
std::string proto_;
|
||||
std::string ip_addr_;
|
||||
};
|
||||
|
||||
private:
|
||||
class NullParent : public TransportClientParent
|
||||
{
|
||||
public:
|
||||
NullParent(TransportClientParent* old_parent)
|
||||
: is_openvpn_protocol(old_parent->transport_is_openvpn_protocol())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void transport_recv(BufferAllocated& buf) {}
|
||||
virtual void transport_needs_send() {}
|
||||
|
||||
virtual void transport_error(const Error::Type fatal_err, const std::string& err_text)
|
||||
{
|
||||
OPENVPN_LOG("TransportRelayFactory: Transport Error in null parent: " << Error::name(fatal_err) << " : " << err_text);
|
||||
}
|
||||
|
||||
virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text)
|
||||
{
|
||||
OPENVPN_LOG("TransportRelayFactory: Proxy Error in null parent: " << Error::name(fatal_err) << " : " << err_text);
|
||||
}
|
||||
|
||||
// Return true if we are transporting OpenVPN protocol
|
||||
virtual bool transport_is_openvpn_protocol() { return is_openvpn_protocol; }
|
||||
|
||||
// progress notifications
|
||||
virtual void transport_pre_resolve() {}
|
||||
virtual void transport_wait_proxy() {}
|
||||
virtual void transport_wait() {}
|
||||
virtual void transport_connecting() {}
|
||||
|
||||
// Return true if keepalive parameter(s) are enabled.
|
||||
virtual bool is_keepalive_enabled() const { return false; }
|
||||
|
||||
// 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)
|
||||
{
|
||||
keepalive_ping = 0;
|
||||
keepalive_timeout = 0;
|
||||
}
|
||||
|
||||
bool is_openvpn_protocol;
|
||||
};
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(openvpn_io::io_context& io_context,
|
||||
TransportClientParent* parent) override
|
||||
{
|
||||
// io_context MUST stay consistent
|
||||
if (&io_context != &io_context_)
|
||||
throw Exception("TransportRelayFactory: inconsistent io_context");
|
||||
|
||||
transport_->transport_reparent(parent);
|
||||
return transport_;
|
||||
}
|
||||
|
||||
virtual bool is_relay() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context_; // only used to verify consistency
|
||||
TransportClient::Ptr transport_; // the persisted transport
|
||||
std::unique_ptr<TransportClientParent> null_parent_; // placeholder for TransportClient parent before reparenting
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,399 @@
|
||||
// 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/>.
|
||||
|
||||
// TCP transport object specialized for client.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_CLIENT_TCPCLI_H
|
||||
#define OPENVPN_TRANSPORT_CLIENT_TCPCLI_H
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/transport/tcplink.hpp>
|
||||
#ifdef OPENVPN_TLS_LINK
|
||||
#include <openvpn/transport/tlslink.hpp>
|
||||
#endif
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/transport/socket_protect.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TCPTransport {
|
||||
|
||||
class ClientConfig : public TransportClientFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
RemoteList::Ptr remote_list;
|
||||
size_t free_list_max_size;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
SocketProtect* socket_protect;
|
||||
|
||||
#ifdef OPENVPN_TLS_LINK
|
||||
bool use_tls = false;
|
||||
std::string tls_ca;
|
||||
#endif
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
Gremlin::Config::Ptr gremlin_config;
|
||||
#endif
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(openvpn_io::io_context& io_context,
|
||||
TransportClientParent* parent);
|
||||
|
||||
private:
|
||||
ClientConfig()
|
||||
: free_list_max_size(8),
|
||||
socket_protect(nullptr)
|
||||
{}
|
||||
};
|
||||
|
||||
class Client : public TransportClient, AsyncResolvableTCP
|
||||
{
|
||||
typedef RCPtr<Client> Ptr;
|
||||
|
||||
typedef Link<openvpn_io::ip::tcp, Client*, false> LinkImpl;
|
||||
#ifdef OPENVPN_TLS_LINK
|
||||
typedef TLSLink<openvpn_io::ip::tcp, Client*, false> LinkImplTLS;
|
||||
#endif
|
||||
|
||||
friend class ClientConfig; // calls constructor
|
||||
friend LinkImpl::Base; // calls tcp_read_handler
|
||||
|
||||
public:
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
stop_requeueing = false;
|
||||
if (config->remote_list->endpoint_available(&server_host,
|
||||
&server_port,
|
||||
&server_protocol))
|
||||
{
|
||||
start_connect_();
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->transport_pre_resolve();
|
||||
|
||||
async_resolve_name(server_host, server_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send_const(buf);
|
||||
}
|
||||
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
bool transport_send_queue_empty() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_empty();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_size();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
const IP::Addr addr = server_endpoint_addr();
|
||||
proto = server_protocol.str();
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
return server_protocol;
|
||||
}
|
||||
|
||||
void stop() override { stop_(); }
|
||||
~Client() override { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TransportClientParent* parent_arg)
|
||||
: AsyncResolvableTCP(io_context_arg),
|
||||
io_context(io_context_arg),
|
||||
socket(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
resolver(io_context_arg),
|
||||
halt(false),
|
||||
stop_requeueing(false)
|
||||
{
|
||||
}
|
||||
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
void transport_stop_requeueing() override
|
||||
{
|
||||
stop_requeueing = true;
|
||||
}
|
||||
|
||||
bool send_const(const Buffer& cbuf)
|
||||
{
|
||||
if (impl)
|
||||
{
|
||||
BufferAllocated buf(cbuf, 0);
|
||||
return impl->send(buf);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool send(BufferAllocated& buf)
|
||||
{
|
||||
if (impl)
|
||||
return impl->send(buf);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void tcp_eof_handler() // called by LinkImpl::Base
|
||||
{
|
||||
config->stats->error(Error::NETWORK_EOF_ERROR);
|
||||
tcp_error_handler("NETWORK_EOF_ERROR");
|
||||
}
|
||||
|
||||
bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl::Base
|
||||
{
|
||||
parent->transport_recv(buf);
|
||||
return !stop_requeueing;
|
||||
}
|
||||
|
||||
void tcp_write_queue_needs_send() // called by LinkImpl::Base
|
||||
{
|
||||
parent->transport_needs_send();
|
||||
}
|
||||
|
||||
void tcp_error_handler(const char *error) // called by LinkImpl::Base
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Transport error on '" << server_host << ": " << error;
|
||||
stop();
|
||||
parent->transport_error(Error::TRANSPORT_ERROR, os.str());
|
||||
}
|
||||
|
||||
void stop_()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
if (impl)
|
||||
impl->stop();
|
||||
|
||||
socket.close();
|
||||
resolver.cancel();
|
||||
async_resolve_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// do DNS resolve
|
||||
void resolve_callback(const openvpn_io::error_code& error,
|
||||
openvpn_io::ip::tcp::resolver::results_type results) override
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// save resolved endpoint list in remote_list
|
||||
config->remote_list->set_endpoint_range(results);
|
||||
start_connect_();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "DNS resolve error on '" << server_host << "' for " << server_protocol.str() << " session: " << error.message();
|
||||
config->stats->error(Error::RESOLVE_ERROR);
|
||||
stop();
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do TCP connect
|
||||
void start_connect_()
|
||||
{
|
||||
config->remote_list->get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via "
|
||||
<< server_protocol.str());
|
||||
parent->transport_wait();
|
||||
socket.open(server_endpoint.protocol());
|
||||
|
||||
if (config->socket_protect)
|
||||
{
|
||||
if (!config->socket_protect->socket_protect(socket.native_handle(), server_endpoint_addr()))
|
||||
{
|
||||
config->stats->error(Error::SOCKET_PROTECT_ERROR);
|
||||
stop();
|
||||
parent->transport_error(Error::UNDEF, "socket_protect error (" + std::string(server_protocol.str()) + ")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
socket.set_option(openvpn_io::ip::tcp::no_delay(true));
|
||||
socket.async_connect(server_endpoint, [self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->start_impl_(error);
|
||||
});
|
||||
}
|
||||
|
||||
// start I/O on TCP socket
|
||||
void start_impl_(const openvpn_io::error_code& error)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
#ifdef OPENVPN_TLS_LINK
|
||||
if (config->use_tls)
|
||||
{
|
||||
int flags = SSLConst::LOG_VERIFY_STATUS|SSLConst::ENABLE_CLIENT_SNI;
|
||||
SSLLib::SSLAPI::Config::Ptr ssl_conf;
|
||||
ssl_conf.reset(new SSLLib::SSLAPI::Config());
|
||||
ssl_conf->set_mode(Mode(Mode::CLIENT));
|
||||
ssl_conf->set_local_cert_enabled(false);
|
||||
ssl_conf->set_frame(config->frame);
|
||||
ssl_conf->set_rng(new SSLLib::RandomAPI(false));
|
||||
|
||||
if (!config->tls_ca.empty())
|
||||
{
|
||||
ssl_conf->load_ca(config->tls_ca, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
flags |= SSLConst::NO_VERIFY_PEER;
|
||||
}
|
||||
|
||||
ssl_conf->set_flags(flags);
|
||||
ssl_factory = ssl_conf->new_factory();
|
||||
|
||||
impl.reset(new LinkImplTLS(this,
|
||||
io_context,
|
||||
socket,
|
||||
0,
|
||||
config->free_list_max_size,
|
||||
config->frame,
|
||||
config->stats,
|
||||
ssl_factory));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
impl.reset(new LinkImpl(this,
|
||||
socket,
|
||||
0, // send_queue_max_size is unlimited because we regulate size in cliproto.hpp
|
||||
config->free_list_max_size,
|
||||
(*config->frame)[Frame::READ_LINK_TCP],
|
||||
config->stats));
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
impl->gremlin_config(config->gremlin_config);
|
||||
#endif
|
||||
impl->start();
|
||||
if (!parent->transport_is_openvpn_protocol())
|
||||
impl->set_raw_mode(true);
|
||||
parent->transport_connecting();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << server_protocol.str() << " connect error on '" << server_host << ':' << server_port << "' (" << server_endpoint << "): " << error.message();
|
||||
config->stats->error(Error::TCP_CONNECT_ERROR);
|
||||
stop();
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string server_host;
|
||||
std::string server_port;
|
||||
Protocol server_protocol;
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
openvpn_io::ip::tcp::socket socket;
|
||||
ClientConfig::Ptr config;
|
||||
TransportClientParent* parent;
|
||||
LinkBase::Ptr impl;
|
||||
openvpn_io::ip::tcp::resolver resolver;
|
||||
LinkImpl::Base::protocol::endpoint server_endpoint;
|
||||
bool halt;
|
||||
bool stop_requeueing;
|
||||
|
||||
#ifdef OPENVPN_TLS_LINK
|
||||
SSLFactoryAPI::Ptr ssl_factory;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline TransportClient::Ptr ClientConfig::new_transport_client_obj(openvpn_io::io_context& io_context,
|
||||
TransportClientParent* parent)
|
||||
{
|
||||
return TransportClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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/>.
|
||||
|
||||
// Abstract base classes for client transport objects that implement UDP, TCP,
|
||||
// HTTP Proxy, etc.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_CLIENT_TRANSBASE_H
|
||||
#define OPENVPN_TRANSPORT_CLIENT_TRANSBASE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/error/error.hpp>
|
||||
#include <openvpn/crypto/cryptodc.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
struct TransportClientParent;
|
||||
|
||||
// Base class for client transport object.
|
||||
struct TransportClient : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<TransportClient> Ptr;
|
||||
|
||||
virtual void transport_start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool transport_send_const(const Buffer& buf) = 0;
|
||||
virtual bool transport_send(BufferAllocated& buf) = 0;
|
||||
virtual bool transport_send_queue_empty() = 0;
|
||||
virtual bool transport_has_send_queue() = 0;
|
||||
virtual void transport_stop_requeueing() = 0;
|
||||
virtual unsigned int transport_send_queue_size() = 0;
|
||||
virtual void reset_align_adjust(const size_t align_adjust) = 0;
|
||||
virtual IP::Addr server_endpoint_addr() const = 0;
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const = 0;
|
||||
virtual Protocol transport_protocol() const = 0;
|
||||
virtual void transport_reparent(TransportClientParent* parent) = 0;
|
||||
};
|
||||
|
||||
// Base class for parent of client transport object, used by client transport
|
||||
// objects to communicate received data packets, exceptions, and progress
|
||||
// notifications.
|
||||
struct TransportClientParent
|
||||
{
|
||||
virtual void transport_recv(BufferAllocated& buf) = 0;
|
||||
virtual void transport_needs_send() = 0; // notification that send queue is empty
|
||||
virtual void transport_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
|
||||
// Return true if we are transporting OpenVPN protocol
|
||||
virtual bool transport_is_openvpn_protocol() = 0;
|
||||
|
||||
// progress notifications
|
||||
virtual void transport_pre_resolve() = 0;
|
||||
virtual void transport_wait_proxy() = 0;
|
||||
virtual void transport_wait() = 0;
|
||||
virtual void transport_connecting() = 0;
|
||||
|
||||
// Return true if keepalive parameter(s) are enabled.
|
||||
virtual bool is_keepalive_enabled() const = 0;
|
||||
|
||||
// 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) = 0;
|
||||
|
||||
virtual ~TransportClientParent() {}
|
||||
};
|
||||
|
||||
// Factory for client transport object.
|
||||
struct TransportClientFactory : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<TransportClientFactory> Ptr;
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(openvpn_io::io_context& io_context,
|
||||
TransportClientParent* parent) = 0;
|
||||
virtual bool is_relay() { return false; }
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TRANSPORT_CLIENT_TRANSBASE_H
|
||||
@@ -0,0 +1,336 @@
|
||||
// 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/>.
|
||||
|
||||
// UDP transport object specialized for client.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_CLIENT_UDPCLI_H
|
||||
#define OPENVPN_TRANSPORT_CLIENT_UDPCLI_H
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/bigmutex.hpp>
|
||||
#include <openvpn/common/likely.hpp>
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/transport/udplink.hpp>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/transport/socket_protect.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace UDPTransport {
|
||||
|
||||
class ClientConfig : public TransportClientFactory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> Ptr;
|
||||
|
||||
RemoteList::Ptr remote_list;
|
||||
bool server_addr_float;
|
||||
bool synchronous_dns_lookup;
|
||||
int n_parallel;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
SocketProtect* socket_protect;
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
Gremlin::Config::Ptr gremlin_config;
|
||||
#endif
|
||||
|
||||
static Ptr new_obj()
|
||||
{
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(openvpn_io::io_context& io_context,
|
||||
TransportClientParent* parent);
|
||||
|
||||
private:
|
||||
ClientConfig()
|
||||
: server_addr_float(false),
|
||||
synchronous_dns_lookup(false),
|
||||
n_parallel(8),
|
||||
socket_protect(nullptr)
|
||||
{}
|
||||
};
|
||||
|
||||
class Client : public TransportClient, AsyncResolvableUDP
|
||||
{
|
||||
typedef RCPtr<Client> Ptr;
|
||||
|
||||
friend class ClientConfig; // calls constructor
|
||||
friend class Link<Client*>; // calls udp_read_handler
|
||||
|
||||
typedef Link<Client*> LinkImpl;
|
||||
|
||||
public:
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
halt = false;
|
||||
if (config->remote_list->endpoint_available(&server_host, &server_port, nullptr))
|
||||
{
|
||||
start_connect_();
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->transport_pre_resolve();
|
||||
|
||||
if (config->synchronous_dns_lookup)
|
||||
{
|
||||
openvpn_io::error_code error;
|
||||
openvpn_io::ip::udp::resolver::results_type results = resolver.resolve(server_host, server_port, error);
|
||||
resolve_callback(error, results);
|
||||
}
|
||||
else
|
||||
{
|
||||
async_resolve_name(server_host, server_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
bool transport_send_queue_empty() override // really only has meaning for TCP
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void transport_stop_requeueing() override { }
|
||||
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
const IP::Addr addr = server_endpoint_addr();
|
||||
proto = "UDP";
|
||||
proto += addr.version_string();
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::UDPv4);
|
||||
else if (server_endpoint.address().is_v6())
|
||||
return Protocol(Protocol::UDPv6);
|
||||
else
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
void stop() override { stop_(); }
|
||||
~Client() override { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TransportClientParent* parent_arg)
|
||||
: AsyncResolvableUDP(io_context_arg),
|
||||
socket(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
resolver(io_context_arg),
|
||||
halt(false)
|
||||
{
|
||||
}
|
||||
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
bool send(const Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
{
|
||||
const int err = impl->send(buf, nullptr);
|
||||
if (unlikely(err))
|
||||
{
|
||||
// While UDP errors are generally ignored, certain
|
||||
// errors should be forwarded up to the higher levels.
|
||||
#ifdef OPENVPN_PLATFORM_IPHONE
|
||||
if (err == EADDRNOTAVAIL)
|
||||
{
|
||||
stop();
|
||||
parent->transport_error(Error::TRANSPORT_ERROR, "EADDRNOTAVAIL: Can't assign requested address");
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void udp_read_handler(PacketFrom::SPtr& pfp) // called by LinkImpl
|
||||
{
|
||||
if (config->server_addr_float || pfp->sender_endpoint == server_endpoint)
|
||||
parent->transport_recv(pfp->buf);
|
||||
else
|
||||
config->stats->error(Error::BAD_SRC_ADDR);
|
||||
}
|
||||
|
||||
void stop_()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
if (impl)
|
||||
impl->stop();
|
||||
socket.close();
|
||||
resolver.cancel();
|
||||
async_resolve_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// called after DNS resolution has succeeded or failed
|
||||
void resolve_callback(const openvpn_io::error_code& error,
|
||||
openvpn_io::ip::udp::resolver::results_type results) override
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// save resolved endpoint list in remote_list
|
||||
config->remote_list->set_endpoint_range(results);
|
||||
start_connect_();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "DNS resolve error on '" << server_host << "' for UDP session: " << error.message();
|
||||
config->stats->error(Error::RESOLVE_ERROR);
|
||||
stop();
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do UDP connect
|
||||
void start_connect_()
|
||||
{
|
||||
config->remote_list->get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via UDP");
|
||||
parent->transport_wait();
|
||||
socket.open(server_endpoint.protocol());
|
||||
|
||||
if (config->socket_protect)
|
||||
{
|
||||
if (!config->socket_protect->socket_protect(socket.native_handle(), server_endpoint_addr()))
|
||||
{
|
||||
config->stats->error(Error::SOCKET_PROTECT_ERROR);
|
||||
stop();
|
||||
parent->transport_error(Error::UNDEF, "socket_protect error (UDP)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
socket.async_connect(server_endpoint, [self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->start_impl_(error);
|
||||
});
|
||||
}
|
||||
|
||||
// start I/O on UDP socket
|
||||
void start_impl_(const openvpn_io::error_code& error)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
impl.reset(new LinkImpl(this,
|
||||
socket,
|
||||
(*config->frame)[Frame::READ_LINK_UDP],
|
||||
config->stats));
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
impl->gremlin_config(config->gremlin_config);
|
||||
#endif
|
||||
impl->start(config->n_parallel);
|
||||
parent->transport_connecting();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "UDP connect error on '" << server_host << ':' << server_port << "' (" << server_endpoint << "): " << error.message();
|
||||
config->stats->error(Error::UDP_CONNECT_ERROR);
|
||||
stop();
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string server_host;
|
||||
std::string server_port;
|
||||
|
||||
openvpn_io::ip::udp::socket socket;
|
||||
ClientConfig::Ptr config;
|
||||
TransportClientParent* parent;
|
||||
LinkImpl::Ptr impl;
|
||||
openvpn_io::ip::udp::resolver resolver;
|
||||
UDPTransport::AsioEndpoint server_endpoint;
|
||||
bool halt;
|
||||
};
|
||||
|
||||
inline TransportClient::Ptr ClientConfig::new_transport_client_obj(openvpn_io::io_context& io_context,
|
||||
TransportClientParent* parent)
|
||||
{
|
||||
return TransportClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_DCO_H
|
||||
#define OPENVPN_TRANSPORT_DCO_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/client/remotelist.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
struct DCO : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<DCO> Ptr;
|
||||
|
||||
struct TransportConfig
|
||||
{
|
||||
TransportConfig()
|
||||
: server_addr_float(false)
|
||||
{
|
||||
}
|
||||
|
||||
Protocol protocol;
|
||||
RemoteList::Ptr remote_list;
|
||||
bool server_addr_float;
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
};
|
||||
|
||||
struct TunConfig
|
||||
{
|
||||
TunConfig()
|
||||
{
|
||||
}
|
||||
|
||||
TunProp::Config tun_prop;
|
||||
Stop* stop = nullptr;
|
||||
};
|
||||
|
||||
virtual TunClientFactory::Ptr new_tun_factory(const TunConfig& conf, const OptionList& opt) = 0;
|
||||
virtual TransportClientFactory::Ptr new_transport_factory(const TransportConfig& conf) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,224 @@
|
||||
// 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_TRANSPORT_GREMLIN_H
|
||||
#define OPENVPN_TRANSPORT_GREMLIN_H
|
||||
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/random/mtrandapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace Gremlin {
|
||||
|
||||
OPENVPN_EXCEPTION(gremlin_error);
|
||||
|
||||
struct DelayedQueue : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<DelayedQueue> Ptr;
|
||||
|
||||
DelayedQueue(openvpn_io::io_context& io_context,
|
||||
const unsigned int delay_ms)
|
||||
: dur(Time::Duration::milliseconds(delay_ms)),
|
||||
next_event(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void queue(F&& func_arg)
|
||||
{
|
||||
const bool empty = events.empty();
|
||||
events.emplace_back(new Event<F>(Time::now() + dur, std::move(func_arg)));
|
||||
if (empty)
|
||||
set_timer();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return events.size();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
next_event.cancel();
|
||||
}
|
||||
|
||||
private:
|
||||
struct EventBase
|
||||
{
|
||||
virtual void call() = 0;
|
||||
virtual const Time& fire_time() = 0;
|
||||
virtual ~EventBase() {}
|
||||
};
|
||||
|
||||
template <class F>
|
||||
struct Event : public EventBase
|
||||
{
|
||||
public:
|
||||
Event(Time fire_arg, F&& func_arg)
|
||||
: fire(fire_arg),
|
||||
func(std::move(func_arg))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void call()
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
virtual const Time& fire_time()
|
||||
{
|
||||
return fire;
|
||||
}
|
||||
|
||||
private:
|
||||
Time fire;
|
||||
F func;
|
||||
};
|
||||
|
||||
void set_timer()
|
||||
{
|
||||
if (events.empty())
|
||||
return;
|
||||
EventBase& ev = *events.front();
|
||||
next_event.expires_at(ev.fire_time());
|
||||
next_event.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
EventBase& ev = *self->events.front();
|
||||
ev.call();
|
||||
self->events.pop_front();
|
||||
self->set_timer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Time::Duration dur;
|
||||
AsioTimer next_event;
|
||||
std::deque<std::unique_ptr<EventBase>> events;
|
||||
};
|
||||
|
||||
class Config : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Config> Ptr;
|
||||
|
||||
Config(const std::string& config_str)
|
||||
{
|
||||
const std::vector<std::string> parms = string::split(config_str, ',');
|
||||
if (parms.size() < 4)
|
||||
throw gremlin_error("need 4 comma-separated values for send_delay_ms, recv_delay_ms, send_drop_prob, recv_drop_prob");
|
||||
if (!parse_number(string::trim_copy(parms[0]), send_delay_ms))
|
||||
throw gremlin_error("send_delay_ms");
|
||||
if (!parse_number(string::trim_copy(parms[1]), recv_delay_ms))
|
||||
throw gremlin_error("recv_delay_ms");
|
||||
if (!parse_number(string::trim_copy(parms[2]), send_drop_probability))
|
||||
throw gremlin_error("send_drop_probability");
|
||||
if (!parse_number(string::trim_copy(parms[3]), recv_drop_probability))
|
||||
throw gremlin_error("recv_drop_probability");
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << '[' << send_delay_ms << ',' << recv_delay_ms << ',' << send_drop_probability << ',' << recv_drop_probability << ']';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
unsigned int send_delay_ms = 0;
|
||||
unsigned int recv_delay_ms = 0;
|
||||
unsigned int send_drop_probability = 0;
|
||||
unsigned int recv_drop_probability = 0;
|
||||
};
|
||||
|
||||
class SendRecvQueue
|
||||
{
|
||||
public:
|
||||
SendRecvQueue(openvpn_io::io_context& io_context,
|
||||
const Config::Ptr& conf_arg,
|
||||
const bool tcp_arg)
|
||||
: conf(conf_arg),
|
||||
send(new DelayedQueue(io_context, conf->send_delay_ms)),
|
||||
recv(new DelayedQueue(io_context, conf->recv_delay_ms)),
|
||||
tcp(tcp_arg)
|
||||
{
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void send_queue(F&& func_arg)
|
||||
{
|
||||
if (tcp || flip(conf->send_drop_probability))
|
||||
send->queue(std::move(func_arg));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void recv_queue(F&& func_arg)
|
||||
{
|
||||
if (tcp || flip(conf->recv_drop_probability))
|
||||
recv->queue(std::move(func_arg));
|
||||
}
|
||||
|
||||
size_t send_size() const
|
||||
{
|
||||
return send->size();
|
||||
}
|
||||
|
||||
size_t recv_size() const
|
||||
{
|
||||
return recv->size();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
send->stop();
|
||||
recv->stop();
|
||||
}
|
||||
|
||||
private:
|
||||
bool flip(const unsigned int prob)
|
||||
{
|
||||
if (prob)
|
||||
return ri.randrange(prob) != 0;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
Config::Ptr conf;
|
||||
MTRand ri;
|
||||
DelayedQueue::Ptr send;
|
||||
DelayedQueue::Ptr recv;
|
||||
bool tcp;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#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-2018 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/ip/ipcommon.hpp>
|
||||
#include <openvpn/ip/ip4.hpp>
|
||||
#include <openvpn/ip/ip6.hpp>
|
||||
#include <openvpn/ip/tcp.hpp>
|
||||
|
||||
#if OPENVPN_DEBUG_PROTO >= 2
|
||||
#define OPENVPN_LOG_MSSFIX(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_MSSFIX(x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
class MSSFix {
|
||||
public:
|
||||
static void mssfix(BufferAllocated& buf, int mss_inter)
|
||||
{
|
||||
if (buf.empty())
|
||||
return;
|
||||
|
||||
switch (IPCommon::version(buf[0]))
|
||||
{
|
||||
case IPCommon::IPv4:
|
||||
{
|
||||
if (buf.length() <= sizeof(struct IPv4Header))
|
||||
break;
|
||||
|
||||
const IPv4Header *iphdr = (const IPv4Header *)buf.c_data();
|
||||
|
||||
auto ipv4hlen = IPv4Header::length(iphdr->version_len);
|
||||
|
||||
if (iphdr->protocol == IPCommon::TCP &&
|
||||
ntohs(iphdr->tot_len) == buf.length() &&
|
||||
(ntohs(iphdr->frag_off) & IPv4Header::OFFMASK) == 0 &&
|
||||
ipv4hlen <= buf.length() &&
|
||||
buf.length() - ipv4hlen >= sizeof(struct TCPHeader))
|
||||
{
|
||||
TCPHeader* tcphdr = (TCPHeader*)(buf.data() + ipv4hlen);
|
||||
int ip_payload_len = buf.length() - ipv4hlen;
|
||||
|
||||
do_mssfix(tcphdr, mss_inter - (sizeof(struct IPv4Header) + sizeof(struct TCPHeader)), ip_payload_len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IPCommon::IPv6:
|
||||
{
|
||||
if (buf.length() <= sizeof(struct IPv6Header))
|
||||
break;
|
||||
|
||||
const IPv6Header *iphdr = (const IPv6Header *)buf.c_data();
|
||||
|
||||
if (buf.length() != ntohs(iphdr->payload_len) + sizeof(struct IPv6Header))
|
||||
break;
|
||||
|
||||
/* follow header chain until we reach final header, then check for TCP
|
||||
*
|
||||
* An IPv6 packet could, theoretically, have a chain of multiple headers
|
||||
* before the final header (TCP, UDP, ...), so we'd need to walk that
|
||||
* chain (see RFC 2460 and RFC 6564 for details).
|
||||
*
|
||||
* In practice, "most typically used" extention headers (AH, routing,
|
||||
* fragment, mobility) are very unlikely to be seen inside an OpenVPN
|
||||
* tun, so for now, we only handle the case of "single next header = TCP"
|
||||
*/
|
||||
if (iphdr->nexthdr != IPCommon::TCP)
|
||||
break;
|
||||
|
||||
/* skip IPv6 header (40 bytes),
|
||||
* verify remainder is large enough to contain a full TCP header
|
||||
*/
|
||||
int payload_len = buf.length() - sizeof(struct IPv6Header);
|
||||
if (payload_len >= (int) sizeof(struct TCPHeader))
|
||||
{
|
||||
TCPHeader *tcphdr = (TCPHeader *)(buf.data() + sizeof(struct IPv6Header));
|
||||
do_mssfix(tcphdr, mss_inter - (sizeof(struct IPv6Header) + sizeof(struct TCPHeader)),
|
||||
payload_len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void do_mssfix(TCPHeader *tcphdr, int max_mss, int ip_payload_len)
|
||||
{
|
||||
if ((tcphdr->flags & TCPHeader::FLAG_SYN) == 0)
|
||||
return;
|
||||
|
||||
int tcphlen = TCPHeader::length(tcphdr->doff_res);
|
||||
if (tcphlen <= (int) sizeof(struct TCPHeader) || tcphlen > ip_payload_len)
|
||||
return;
|
||||
|
||||
int olen, optlen; // length of options field and Option-Length
|
||||
uint8_t *opt; // option type
|
||||
|
||||
for (olen = tcphlen - sizeof(struct TCPHeader), opt = (uint8_t *)(tcphdr + 1);
|
||||
olen > 1;
|
||||
olen -= optlen, opt += optlen)
|
||||
{
|
||||
if (*opt == TCPHeader::OPT_EOL)
|
||||
break;
|
||||
else if (*opt == TCPHeader::OPT_NOP)
|
||||
optlen = 1;
|
||||
else
|
||||
{
|
||||
optlen = *(opt + 1);
|
||||
if (optlen <= 0 || optlen > olen)
|
||||
break;
|
||||
if ((*opt == TCPHeader::OPT_MAXSEG) && (optlen == TCPHeader::OPTLEN_MAXSEG))
|
||||
{
|
||||
uint16_t mssval = (opt[2] << 8) + opt[3];
|
||||
if (mssval > max_mss)
|
||||
{
|
||||
OPENVPN_LOG_MSSFIX("MTU MSS " << mssval << " -> " << max_mss);
|
||||
int accumulate = htons(mssval);
|
||||
opt[2] = (max_mss >> 8) & 0xff;
|
||||
opt[3] = max_mss & 0xff;
|
||||
accumulate -= htons(max_mss);
|
||||
tcp_adjust_checksum(accumulate, tcphdr->check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_MUTATE_H
|
||||
#define OPENVPN_TRANSPORT_MUTATE_H
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class TransportMutateStream : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<TransportMutateStream> Ptr;
|
||||
|
||||
virtual void pre_send(BufferAllocated& buf) = 0;
|
||||
virtual void post_recv(BufferAllocated& buf) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,163 @@
|
||||
// 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_TRANSPORT_PKTSTREAM_H
|
||||
#define OPENVPN_TRANSPORT_PKTSTREAM_H
|
||||
|
||||
#include <algorithm> // for std::min
|
||||
#include <cstdint> // for std::uint16_t, etc.
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Used to encapsulate OpenVPN packets onto a stream transport such as TCP,
|
||||
// or extract them from the stream.
|
||||
class PacketStream
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(embedded_packet_size_error);
|
||||
OPENVPN_SIMPLE_EXCEPTION(packet_not_fully_formed);
|
||||
|
||||
PacketStream() : declared_size_defined(false) {}
|
||||
|
||||
// Add stream fragment to packet that we are building up.
|
||||
// Data will be read from buf. On return buf may still contain
|
||||
// residual data. If function is able to use all of buf, it may
|
||||
// grab ownership of it, replacing buf as returned to caller with
|
||||
// an empty (but possibly pre-allocated) BufferAllocated object.
|
||||
void put(BufferAllocated& buf, const Frame::Context& frame_context)
|
||||
{
|
||||
if (buf.defined())
|
||||
{
|
||||
if (!declared_size_defined && !buffer.defined())
|
||||
{
|
||||
if (size_defined(buf))
|
||||
{
|
||||
extract_size(buf, frame_context);
|
||||
if (buf.size() == declared_size) // packet is correctly sized
|
||||
buffer.swap(buf);
|
||||
else if (buf.size() < declared_size) // packet is undersized
|
||||
{
|
||||
if (buf.offset() + declared_size + frame_context.tailroom() <= buf.capacity())
|
||||
buffer.swap(buf);
|
||||
else
|
||||
{
|
||||
buffer.swap(buf);
|
||||
frame_context.realign(buffer);
|
||||
}
|
||||
}
|
||||
else // packet is oversized
|
||||
{
|
||||
frame_context.prepare(buffer);
|
||||
const unsigned char *data = buf.read_alloc(declared_size);
|
||||
buffer.write(data, declared_size);
|
||||
}
|
||||
}
|
||||
else // rare case where packet fragment is too small to contain embedded size
|
||||
{
|
||||
buffer.swap(buf);
|
||||
frame_context.realign(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!declared_size_defined)
|
||||
{
|
||||
if (buf.empty())
|
||||
return;
|
||||
buffer.push_back(buf.pop_front());
|
||||
if (size_defined(buffer))
|
||||
extract_size(buffer, frame_context);
|
||||
}
|
||||
if (buffer.size() < declared_size)
|
||||
{
|
||||
const size_t needed = std::min(declared_size - buffer.size(), buf.size());
|
||||
const unsigned char *data = buf.read_alloc(needed);
|
||||
buffer.write(data, needed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if get() may be called to return fully formed packet
|
||||
bool ready() const
|
||||
{
|
||||
return declared_size_defined && buffer.size() >= declared_size;
|
||||
}
|
||||
|
||||
// return fully formed packet as ret. ret, as passed to method, will
|
||||
// be grabbed, reset, and subsequently used internally.
|
||||
void get(BufferAllocated& ret)
|
||||
{
|
||||
if (declared_size_defined && buffer.size() == declared_size)
|
||||
{
|
||||
ret.swap(buffer);
|
||||
buffer.reset_content();
|
||||
declared_size_defined = false;
|
||||
}
|
||||
else
|
||||
throw packet_not_fully_formed();
|
||||
}
|
||||
|
||||
// prepend uint16_t size to buffer
|
||||
static void prepend_size(Buffer& buf)
|
||||
{
|
||||
const std::uint16_t net_len = htons(buf.size());
|
||||
buf.prepend((const unsigned char *)&net_len, sizeof(net_len));
|
||||
}
|
||||
|
||||
private:
|
||||
void extract_size(Buffer& buf, const Frame::Context& frame_context)
|
||||
{
|
||||
const size_t size = read_size(buf);
|
||||
validate_size(size, frame_context);
|
||||
declared_size = size;
|
||||
declared_size_defined = true;
|
||||
}
|
||||
|
||||
static bool size_defined(const Buffer& buf)
|
||||
{
|
||||
return buf.size() >= sizeof(std::uint16_t);
|
||||
}
|
||||
|
||||
static size_t read_size(Buffer& buf)
|
||||
{
|
||||
std::uint16_t net_len;
|
||||
buf.read((unsigned char *)&net_len, sizeof(net_len));
|
||||
return ntohs(net_len);
|
||||
}
|
||||
|
||||
static void validate_size(const size_t size, const Frame::Context& frame_context)
|
||||
{
|
||||
if (!size || size > frame_context.payload())
|
||||
throw embedded_packet_size_error();
|
||||
}
|
||||
|
||||
size_t declared_size; // declared size of packet in leading uint16_t prefix
|
||||
bool declared_size_defined; // true if declared_size is defined
|
||||
BufferAllocated buffer; // accumulated packet data
|
||||
};
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TRANSPORT_PKTSTREAM_H
|
||||
@@ -0,0 +1,314 @@
|
||||
// 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_TRANSPORT_PROTOCOL_H
|
||||
#define OPENVPN_TRANSPORT_PROTOCOL_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint> // for std::uint32_t, etc.
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/option_error.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// A class that encapsulates a transport protocol.
|
||||
class Protocol
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
NONE,
|
||||
UDPv4,
|
||||
TCPv4,
|
||||
UDPv6,
|
||||
TCPv6,
|
||||
TLSv4, // TLS over IPv4
|
||||
TLSv6, // TLS over IPv6
|
||||
UnixStream, // unix domain socket (stream)
|
||||
UnixDGram, // unix domain socket (datagram)
|
||||
NamedPipe, // named pipe (Windows only)
|
||||
UDP=UDPv4,
|
||||
TCP=TCPv4,
|
||||
TLS=TLSv4,
|
||||
};
|
||||
|
||||
enum AllowSuffix {
|
||||
NO_SUFFIX,
|
||||
CLIENT_SUFFIX,
|
||||
SERVER_SUFFIX,
|
||||
};
|
||||
|
||||
Protocol() : type_(NONE) {}
|
||||
explicit Protocol(const Type t) : type_(t) {}
|
||||
Type operator()() const { return type_; }
|
||||
|
||||
bool defined() const { return type_ != NONE; }
|
||||
|
||||
void reset() { type_ = NONE; }
|
||||
|
||||
bool is_udp() const { return type_ == UDPv4 || type_ == UDPv6; }
|
||||
bool is_tcp() const { return type_ == TCPv4 || type_ == TCPv6; }
|
||||
bool is_tls() const { return type_ == TLSv4 || type_ == TLSv6; }
|
||||
bool is_reliable() const { return is_tcp() || is_tls(); }
|
||||
bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6 || type_ == TLSv6; }
|
||||
bool is_unix() const { return type_ == UnixStream || type_ == UnixDGram; }
|
||||
bool is_named_pipe() const { return type_ == NamedPipe; }
|
||||
bool is_local() const { return is_unix() || is_named_pipe(); }
|
||||
|
||||
bool operator==(const Protocol& other) const
|
||||
{
|
||||
return type_ == other.type_;
|
||||
}
|
||||
|
||||
bool operator!=(const Protocol& other) const
|
||||
{
|
||||
return type_ != other.type_;
|
||||
}
|
||||
|
||||
bool transport_match(const Protocol& other) const
|
||||
{
|
||||
return transport_proto() == other.transport_proto();
|
||||
}
|
||||
|
||||
unsigned int extra_transport_bytes() const
|
||||
{
|
||||
return (is_tcp() || is_tls()) ? sizeof(std::uint16_t) : 0;
|
||||
}
|
||||
|
||||
void mod_addr_version(const IP::Addr& addr)
|
||||
{
|
||||
switch (addr.version())
|
||||
{
|
||||
case IP::Addr::UNSPEC:
|
||||
break;
|
||||
case IP::Addr::V4:
|
||||
if (is_udp())
|
||||
type_ = UDPv4;
|
||||
else if (is_tcp())
|
||||
type_ = TCPv4;
|
||||
else if (is_tls())
|
||||
type_ = TLSv4;
|
||||
break;
|
||||
case IP::Addr::V6:
|
||||
if (is_udp())
|
||||
type_ = UDPv6;
|
||||
else if (is_tcp())
|
||||
type_ = TCPv6;
|
||||
else if (is_tls())
|
||||
type_ = TLSv6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Protocol parse(const std::string& str,
|
||||
const AllowSuffix allow_suffix,
|
||||
const char *title = nullptr)
|
||||
{
|
||||
Protocol ret;
|
||||
if (string::strcasecmp(str, "adaptive") == 0)
|
||||
return ret;
|
||||
ret.type_ = parse_type(str, allow_suffix);
|
||||
if (ret.type_ == NONE)
|
||||
{
|
||||
if (!title)
|
||||
title = "protocol";
|
||||
OPENVPN_THROW(option_error, "error parsing " << title << ": " << str);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool is_local_type(const std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return false;
|
||||
if (str[0] != 'u' && str[0] != 'U' // unix fast path
|
||||
&& str[0] != 'n' && str[0] != 'N') // named pipe fast path
|
||||
return false;
|
||||
const Type type = parse_type(str, NO_SUFFIX);
|
||||
return type == UnixStream || type == UnixDGram || type == NamedPipe;
|
||||
}
|
||||
|
||||
int transport_proto() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case UDPv4:
|
||||
return 0;
|
||||
case TCPv4:
|
||||
return 1;
|
||||
case UDPv6:
|
||||
return 0;
|
||||
case TCPv6:
|
||||
return 1;
|
||||
case UnixDGram:
|
||||
return 2;
|
||||
case UnixStream:
|
||||
return 3;
|
||||
case NamedPipe:
|
||||
return 4;
|
||||
case TLSv4:
|
||||
case TLSv6:
|
||||
return 5;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *str() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case UDPv4:
|
||||
return "UDPv4";
|
||||
case TCPv4:
|
||||
return "TCPv4";
|
||||
case UDPv6:
|
||||
return "UDPv6";
|
||||
case TCPv6:
|
||||
return "TCPv6";
|
||||
case TLSv4:
|
||||
return "TLS/TCPv4";
|
||||
case TLSv6:
|
||||
return "TLS/TCPv6";
|
||||
case UnixStream:
|
||||
return "UnixStream";
|
||||
case UnixDGram:
|
||||
return "UnixDGram";
|
||||
case NamedPipe:
|
||||
return "NamedPipe";
|
||||
default:
|
||||
return "UNDEF_PROTO";
|
||||
}
|
||||
}
|
||||
|
||||
/* This function returns a parseable string representation of the used
|
||||
* transport protocol. NOTE: returns nullptr if there is no mapping */
|
||||
const char *protocol_to_string() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case UDPv4:
|
||||
return "udp4";
|
||||
case TCPv4:
|
||||
return "tcp4";
|
||||
case UDPv6:
|
||||
return "udp6";
|
||||
case TCPv6:
|
||||
return "tcp6";
|
||||
case TLSv4:
|
||||
return "tls4";
|
||||
case TLSv6:
|
||||
return "tls6";
|
||||
case UnixStream:
|
||||
return "unix-stream";
|
||||
case UnixDGram:
|
||||
return "unix-dgram";
|
||||
case NamedPipe:
|
||||
return "named-pipe";
|
||||
case NONE:
|
||||
return "adaptive";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char *str_client(const bool force_ipv4) const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case UDPv4:
|
||||
return "UDPv4";
|
||||
case TCPv4:
|
||||
return "TCPv4_CLIENT";
|
||||
case UDPv6:
|
||||
return force_ipv4 ? "UDPv4" : "UDPv6";
|
||||
case TCPv6:
|
||||
return force_ipv4 ? "TCPv4_CLIENT" : "TCPv6_CLIENT";
|
||||
case TLSv4:
|
||||
return "TLSv4";
|
||||
case TLSv6:
|
||||
return force_ipv4 ? "TLSv4" : "TLSv6";
|
||||
default:
|
||||
return "UNDEF_PROTO";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static Type parse_type(const std::string& str,
|
||||
const AllowSuffix allow_suffix)
|
||||
{
|
||||
Type ret = NONE;
|
||||
std::string s = str;
|
||||
string::to_lower(s);
|
||||
switch (allow_suffix)
|
||||
{
|
||||
case NO_SUFFIX:
|
||||
break;
|
||||
case CLIENT_SUFFIX:
|
||||
if (string::ends_with(s, "-client"))
|
||||
s = s.substr(0, s.length()-7);
|
||||
break;
|
||||
case SERVER_SUFFIX:
|
||||
if (string::ends_with(s, "-server"))
|
||||
s = s.substr(0, s.length()-7);
|
||||
break;
|
||||
}
|
||||
if (string::starts_with(s, "unix")) // unix domain socket
|
||||
{
|
||||
if (s == "unix-stream")
|
||||
ret = UnixStream;
|
||||
else if (s == "unix-dgram")
|
||||
ret = UnixDGram;
|
||||
}
|
||||
else if (s == "named-pipe") // Windows named pipe
|
||||
ret = NamedPipe;
|
||||
else if (s.length() >= 3) // udp/tcp
|
||||
{
|
||||
const std::string s1 = s.substr(0, 3);
|
||||
const std::string s2 = s.substr(3);
|
||||
if (s2 == "" || s2 == "4" || s2 == "v4")
|
||||
{
|
||||
if (s1 == "udp")
|
||||
ret = UDPv4;
|
||||
else if (s1 == "tcp")
|
||||
ret = TCPv4;
|
||||
else if (s1 == "tls")
|
||||
ret = TLSv4;
|
||||
}
|
||||
else if (s2 == "6" || s2 == "v6")
|
||||
{
|
||||
if (s1 == "udp")
|
||||
ret = UDPv6;
|
||||
else if (s1 == "tcp")
|
||||
ret = TCPv6;
|
||||
else if (s1 == "tls")
|
||||
ret = TLSv6;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Type type_;
|
||||
};
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_TRANSPORT_PROTOCOL_H
|
||||
@@ -0,0 +1,36 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_RECONNECT_NOTIFY_H
|
||||
#define OPENVPN_TRANSPORT_RECONNECT_NOTIFY_H
|
||||
|
||||
namespace openvpn {
|
||||
class ReconnectNotify {
|
||||
public:
|
||||
// When a connection is close to timeout, the core will call this
|
||||
// method. If it returns false, the core will disconnect with a
|
||||
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
|
||||
// state.
|
||||
virtual bool pause_on_connection_timeout() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#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/>.
|
||||
|
||||
// Abstract base classes for server transport objects that implement UDP, TCP,
|
||||
// HTTP Proxy, etc.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_SERVER_TRANSBASE_H
|
||||
#define OPENVPN_TRANSPORT_SERVER_TRANSBASE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/crypto/cryptodc.hpp>
|
||||
#include <openvpn/tun/server/tunbase.hpp>
|
||||
#include <openvpn/server/servhalt.hpp>
|
||||
#include <openvpn/server/peerstats.hpp>
|
||||
#include <openvpn/server/peeraddr.hpp>
|
||||
#include <openvpn/ssl/datalimit.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Base class for server transport object.
|
||||
struct TransportServer : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<TransportServer> Ptr;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual std::string local_endpoint_info() const = 0;
|
||||
virtual IP::Addr local_endpoint_addr() const = 0;
|
||||
};
|
||||
|
||||
// Factory for server transport object.
|
||||
struct TransportServerFactory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<TransportServerFactory> Ptr;
|
||||
|
||||
virtual TransportServer::Ptr new_server_obj(openvpn_io::io_context& io_context) = 0;
|
||||
};
|
||||
|
||||
namespace TransportClientInstance {
|
||||
|
||||
// Base class for the per-client-instance state of the TransportServer.
|
||||
// Each client instance uses this class to send data to the transport layer.
|
||||
struct Send : public virtual RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Send> Ptr;
|
||||
|
||||
virtual bool defined() const = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf) = 0;
|
||||
virtual bool transport_send(BufferAllocated& buf) = 0;
|
||||
|
||||
virtual const std::string& transport_info() const = 0;
|
||||
|
||||
// bandwidth stats polling
|
||||
virtual bool stats_pending() const = 0;
|
||||
virtual PeerStats stats_poll() = 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 bool defined() const = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void start(const Send::Ptr& parent,
|
||||
const PeerAddr::Ptr& addr,
|
||||
const int local_peer_id) = 0;
|
||||
|
||||
// Called with OpenVPN-encapsulated packets from transport layer.
|
||||
// Returns true if packet successfully validated.
|
||||
virtual bool transport_recv(BufferAllocated& buf) = 0;
|
||||
|
||||
// Return true if keepalive parameter(s) are enabled.
|
||||
virtual bool is_keepalive_enabled() const = 0;
|
||||
|
||||
// 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) = 0;
|
||||
|
||||
// override the data channel factory
|
||||
virtual void override_dc_factory(const CryptoDCFactory::Ptr& dc_factory) = 0;
|
||||
|
||||
// override the tun provider
|
||||
virtual TunClientInstance::Recv* override_tun(TunClientInstance::Send* tun) = 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;
|
||||
|
||||
// Data limit notification -- trigger a renegotiation
|
||||
// when cdl_status == DataLimit::Red.
|
||||
virtual void data_limit_notify(const int key_id,
|
||||
const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status) = 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;
|
||||
|
||||
};
|
||||
|
||||
// Base class for factory used to create Recv objects.
|
||||
struct Factory : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Factory> Ptr;
|
||||
|
||||
virtual Recv::Ptr new_client_instance() = 0;
|
||||
virtual bool validate_initial_packet(const BufferAllocated& net_buf) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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_TRANSPORT_SOCKET_PROTECT_H
|
||||
#define OPENVPN_TRANSPORT_SOCKET_PROTECT_H
|
||||
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#ifdef OPENVPN_PLATFORM_UWP
|
||||
#include <openvpn/transport/uwp_socket_protect.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
// Used as an interface in cases where the high-level controlling app
|
||||
// needs early access to newly created transport sockets for making
|
||||
// property changes. For example, on Android, we need to "protect"
|
||||
// the socket from being routed into the VPN tunnel.
|
||||
class BaseSocketProtect {
|
||||
public:
|
||||
virtual bool socket_protect(int socket, IP::Addr endpoint) = 0;
|
||||
};
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_UWP
|
||||
typedef UWPSocketProtect SocketProtect;
|
||||
#else
|
||||
typedef BaseSocketProtect SocketProtect;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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 TCP transport object.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_TCPLINK_H
|
||||
#define OPENVPN_TRANSPORT_TCPLINK_H
|
||||
|
||||
#include <deque>
|
||||
#include <utility> // for std::move
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/bigmutex.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/transport/pktstream.hpp>
|
||||
#include <openvpn/transport/mutate.hpp>
|
||||
#include <openvpn/transport/tcplinkcommon.hpp>
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
#include <openvpn/transport/gremlin.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TCPTransport {
|
||||
|
||||
template <typename Protocol, typename ReadHandler, bool RAW_MODE_ONLY>
|
||||
class Link : public LinkCommon<Protocol,
|
||||
ReadHandler,
|
||||
RAW_MODE_ONLY>
|
||||
{
|
||||
typedef std::deque<BufferPtr> Queue;
|
||||
|
||||
public:
|
||||
typedef LinkCommon<Protocol,
|
||||
ReadHandler,
|
||||
RAW_MODE_ONLY> Base;
|
||||
typedef RCPtr<Link> Ptr;
|
||||
|
||||
typedef Protocol protocol;
|
||||
|
||||
friend Base;
|
||||
|
||||
Link(ReadHandler read_handler_arg,
|
||||
typename Protocol::socket& socket_arg,
|
||||
const size_t send_queue_max_size_arg, // 0 to disable
|
||||
const size_t free_list_max_size_arg,
|
||||
const Frame::Context& frame_context_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
: Base(read_handler_arg, socket_arg, send_queue_max_size_arg,
|
||||
free_list_max_size_arg, frame_context_arg, stats_arg)
|
||||
{ }
|
||||
|
||||
private:
|
||||
// Called by LinkCommon
|
||||
virtual void from_app_send_buffer(BufferPtr& buf) override
|
||||
{
|
||||
Base::queue_send_buffer(buf);
|
||||
}
|
||||
|
||||
virtual void recv_buffer(PacketFrom::SPtr& pfp, const size_t bytes_recvd) override
|
||||
{
|
||||
bool requeue = true;
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("TCP recv raw=" << Base::raw_mode_read << " size=" << bytes_recvd);
|
||||
|
||||
pfp->buf.set_size(bytes_recvd);
|
||||
requeue = Base::process_recv_buffer(pfp->buf);
|
||||
if (!Base::halt && requeue)
|
||||
Base::queue_recv(pfp.release()); // reuse PacketFrom object
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
|
||||
// Base class for generic link objects.
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace openvpn
|
||||
{
|
||||
namespace TCPTransport
|
||||
{
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
};
|
||||
|
||||
class LinkBase : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
protected:
|
||||
virtual void recv_buffer(PacketFrom::SPtr& pfp,
|
||||
const size_t bytes_recvd) = 0;
|
||||
virtual void from_app_send_buffer(BufferPtr& buf) = 0;
|
||||
|
||||
public:
|
||||
typedef RCPtr<LinkBase> Ptr;
|
||||
|
||||
virtual bool send_queue_empty() const = 0;
|
||||
virtual unsigned int send_queue_size() const = 0;
|
||||
virtual void reset_align_adjust(const size_t align_adjust) = 0;
|
||||
virtual bool send(BufferAllocated& b) = 0;
|
||||
virtual void set_raw_mode(const bool mode) = 0;
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
// Copyright (C) 2012-2018 OpenVPN Inc.
|
||||
|
||||
// Base class for TCP link objects.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_COMMONLINK_H
|
||||
#define OPENVPN_TRANSPORT_COMMONLINK_H
|
||||
|
||||
#include <deque>
|
||||
#include <utility> // for std::move
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/transport/tcplinkbase.hpp>
|
||||
#include <openvpn/transport/pktstream.hpp>
|
||||
#include <openvpn/transport/mutate.hpp>
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
#include <openvpn/transport/gremlin.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1
|
||||
#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_TCPLINK_ERROR(x)
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 3
|
||||
#define OPENVPN_LOG_TCPLINK_VERBOSE(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_TCPLINK_VERBOSE(x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace TCPTransport {
|
||||
|
||||
template <typename Protocol,
|
||||
typename ReadHandler,
|
||||
bool RAW_MODE_ONLY>
|
||||
class LinkCommon : public LinkBase
|
||||
{
|
||||
typedef std::deque<BufferPtr> Queue;
|
||||
|
||||
public:
|
||||
typedef RCPtr<LinkCommon<Protocol, ReadHandler, RAW_MODE_ONLY>> Ptr;
|
||||
typedef Protocol protocol;
|
||||
|
||||
// In raw mode, data is sent and received without any special encapsulation.
|
||||
// In non-raw mode, data is packetized by prepending a 16-bit length word
|
||||
// onto each packet. The OpenVPN protocol runs in non-raw mode, while other
|
||||
// TCP protocols such as HTTP or HTTPS would run in raw mode.
|
||||
// This method is a no-op if RAW_MODE_ONLY is true.
|
||||
void set_raw_mode(const bool mode)
|
||||
{
|
||||
set_raw_mode_read(mode);
|
||||
set_raw_mode_write(mode);
|
||||
}
|
||||
|
||||
void set_raw_mode_read(const bool mode)
|
||||
{
|
||||
if (RAW_MODE_ONLY)
|
||||
raw_mode_read = true;
|
||||
else
|
||||
raw_mode_read = mode;
|
||||
}
|
||||
|
||||
void set_raw_mode_write(const bool mode)
|
||||
{
|
||||
if (RAW_MODE_ONLY)
|
||||
raw_mode_write = true;
|
||||
else
|
||||
raw_mode_write = mode;
|
||||
}
|
||||
|
||||
void set_mutate(const TransportMutateStream::Ptr& mutate_arg)
|
||||
{
|
||||
mutate = mutate_arg;
|
||||
}
|
||||
|
||||
bool send_queue_empty() const
|
||||
{
|
||||
return send_queue_size() == 0;
|
||||
}
|
||||
|
||||
void inject(const Buffer& src)
|
||||
{
|
||||
const size_t size = src.size();
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("TCP inject size=" << size);
|
||||
if (size && !RAW_MODE_ONLY)
|
||||
{
|
||||
BufferAllocated buf;
|
||||
frame_context.prepare(buf);
|
||||
buf.write(src.c_data(), size);
|
||||
BufferAllocated pkt;
|
||||
put_pktstream(buf, pkt);
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
if (!halt)
|
||||
queue_recv(nullptr);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
halt = true;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
gremlin->stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
void reset_align_adjust(const size_t align_adjust)
|
||||
{
|
||||
frame_context.reset_align_adjust(align_adjust + (is_raw_mode() ? 0 : 2));
|
||||
}
|
||||
|
||||
unsigned int send_queue_size() const
|
||||
{
|
||||
return queue.size()
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
+ (gremlin ? gremlin->send_size() : 0)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
bool send(BufferAllocated& b)
|
||||
{
|
||||
if (halt)
|
||||
return false;
|
||||
|
||||
if (send_queue_max_size && send_queue_size() >= send_queue_max_size)
|
||||
{
|
||||
stats->error(Error::TCP_OVERFLOW);
|
||||
read_handler->tcp_error_handler("TCP_OVERFLOW");
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferPtr buf;
|
||||
if (!free_list.empty())
|
||||
{
|
||||
buf = free_list.front();
|
||||
free_list.pop_front();
|
||||
}
|
||||
else
|
||||
buf.reset(new BufferAllocated());
|
||||
buf->swap(b);
|
||||
if (!is_raw_mode_write())
|
||||
PacketStream::prepend_size(*buf);
|
||||
if (mutate)
|
||||
mutate->pre_send(*buf);
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
gremlin_queue_send_buffer(buf);
|
||||
else
|
||||
#endif
|
||||
from_app_send_buffer(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void queue_recv(PacketFrom *tcpfrom)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::queue_recv");
|
||||
if (!tcpfrom)
|
||||
tcpfrom = new PacketFrom();
|
||||
frame_context.prepare(tcpfrom->buf);
|
||||
|
||||
socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf),
|
||||
[self=Ptr(this), tcpfrom=PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
try
|
||||
{
|
||||
self->handle_recv(std::move(tcpfrom), error, bytes_recvd);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Error::Type err = Error::TCP_SIZE_ERROR;
|
||||
const char *msg = "TCP_SIZE_ERROR";
|
||||
// if exception is an ExceptionCode, translate the code
|
||||
// to return status string
|
||||
{
|
||||
const ExceptionCode *ec = dynamic_cast<const ExceptionCode *>(&e);
|
||||
if (ec && ec->code_defined())
|
||||
{
|
||||
err = ec->code();
|
||||
msg = ec->what();
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what());
|
||||
self->stats->error(err);
|
||||
self->read_handler->tcp_error_handler(msg);
|
||||
self->stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
LinkCommon(ReadHandler read_handler_arg,
|
||||
typename Protocol::socket& socket_arg,
|
||||
const size_t send_queue_max_size_arg, // 0 to disable
|
||||
const size_t free_list_max_size_arg,
|
||||
const Frame::Context& frame_context_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
: socket(socket_arg),
|
||||
halt(false),
|
||||
read_handler(read_handler_arg),
|
||||
frame_context(frame_context_arg),
|
||||
stats(stats_arg),
|
||||
send_queue_max_size(send_queue_max_size_arg),
|
||||
free_list_max_size(free_list_max_size_arg)
|
||||
{
|
||||
set_raw_mode(false);
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
void gremlin_config(const Gremlin::Config::Ptr& config)
|
||||
{
|
||||
if (config)
|
||||
gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, true));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_raw_mode() const {
|
||||
return is_raw_mode_read() && is_raw_mode_write();
|
||||
}
|
||||
|
||||
bool is_raw_mode_read() const {
|
||||
if (RAW_MODE_ONLY)
|
||||
return true;
|
||||
else
|
||||
return raw_mode_read;
|
||||
}
|
||||
|
||||
bool is_raw_mode_write() const {
|
||||
if (RAW_MODE_ONLY)
|
||||
return true;
|
||||
else
|
||||
return raw_mode_write;
|
||||
}
|
||||
|
||||
LinkCommon() { stop(); }
|
||||
|
||||
void queue_send_buffer(BufferPtr& buf)
|
||||
{
|
||||
queue.push_back(std::move(buf));
|
||||
if (queue.size() == 1) // send operation not currently active?
|
||||
queue_send();
|
||||
}
|
||||
|
||||
void queue_send()
|
||||
{
|
||||
BufferAllocated& buf = *queue.front();
|
||||
socket.async_send(buf.const_buffer_clamp(),
|
||||
[self=Ptr(this)](const openvpn_io::error_code& error, const size_t bytes_sent)
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->handle_send(error, bytes_sent);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_send(const openvpn_io::error_code& error, const size_t bytes_sent)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("TLS-TCP send raw=" << raw_mode_write << " size=" << bytes_sent);
|
||||
stats->inc_stat(SessionStats::BYTES_OUT, bytes_sent);
|
||||
stats->inc_stat(SessionStats::PACKETS_OUT, 1);
|
||||
|
||||
BufferPtr buf = queue.front();
|
||||
if (bytes_sent == buf->size())
|
||||
{
|
||||
queue.pop_front();
|
||||
if (free_list.size() < free_list_max_size)
|
||||
{
|
||||
buf->reset_content();
|
||||
free_list.push_back(std::move(buf)); // recycle the buffer for later use
|
||||
}
|
||||
}
|
||||
else if (bytes_sent < buf->size())
|
||||
buf->advance(bytes_sent);
|
||||
else
|
||||
{
|
||||
stats->error(Error::TCP_OVERFLOW);
|
||||
read_handler->tcp_error_handler("TCP_INTERNAL_ERROR"); // error sent more bytes than we asked for
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP send error: " << error.message());
|
||||
stats->error(Error::NETWORK_SEND_ERROR);
|
||||
read_handler->tcp_error_handler("NETWORK_SEND_ERROR");
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
if (!queue.empty())
|
||||
queue_send();
|
||||
else
|
||||
tcp_write_queue_needs_send();
|
||||
}
|
||||
}
|
||||
|
||||
bool process_recv_buffer(BufferAllocated& buf)
|
||||
{
|
||||
bool requeue = true;
|
||||
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::process_recv_buffer: size=" << buf.size());
|
||||
|
||||
if (!is_raw_mode_read())
|
||||
{
|
||||
try {
|
||||
BufferAllocated pkt;
|
||||
requeue = put_pktstream(buf, pkt);
|
||||
if (!buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer
|
||||
buf.move(pkt);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP packet extract error: " << e.what());
|
||||
stats->error(Error::TCP_SIZE_ERROR);
|
||||
read_handler->tcp_error_handler("TCP_SIZE_ERROR");
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mutate)
|
||||
mutate->post_recv(buf);
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
requeue = gremlin_recv(buf);
|
||||
else
|
||||
#endif
|
||||
requeue = read_handler->tcp_read_handler(buf);
|
||||
}
|
||||
|
||||
return requeue;
|
||||
}
|
||||
|
||||
void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_VERBOSE("Link::handle_recv: " << error.message());
|
||||
if (!halt)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
recv_buffer(pfp, bytes_recvd);
|
||||
}
|
||||
else if (error == openvpn_io::error::eof)
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TCP recv EOF");
|
||||
read_handler->tcp_eof_handler();
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_TCPLINK_ERROR("TCP recv error: " << error.message());
|
||||
stats->error(Error::NETWORK_RECV_ERROR);
|
||||
read_handler->tcp_error_handler("NETWORK_RECV_ERROR");
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool put_pktstream(BufferAllocated& buf, BufferAllocated& pkt)
|
||||
{
|
||||
bool requeue = true;
|
||||
stats->inc_stat(SessionStats::BYTES_IN, buf.size());
|
||||
stats->inc_stat(SessionStats::PACKETS_IN, 1);
|
||||
if (mutate)
|
||||
mutate->post_recv(buf);
|
||||
while (buf.size())
|
||||
{
|
||||
pktstream.put(buf, frame_context);
|
||||
if (pktstream.ready())
|
||||
{
|
||||
pktstream.get(pkt);
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
requeue = gremlin_recv(pkt);
|
||||
else
|
||||
#endif
|
||||
requeue = read_handler->tcp_read_handler(pkt);
|
||||
}
|
||||
}
|
||||
return requeue;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
void gremlin_queue_send_buffer(BufferPtr& buf)
|
||||
{
|
||||
gremlin->send_queue([self=Ptr(this), buf=std::move(buf)]() mutable {
|
||||
if (!self->halt)
|
||||
{
|
||||
self->queue_send_buffer(buf);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool gremlin_recv(BufferAllocated& buf)
|
||||
{
|
||||
gremlin->recv_queue([self=Ptr(this), buf=std::move(buf)]() mutable {
|
||||
if (!self->halt)
|
||||
{
|
||||
const bool requeue = self->read_handler->tcp_read_handler(buf);
|
||||
if (requeue)
|
||||
self->queue_recv(nullptr);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void tcp_write_queue_needs_send()
|
||||
{
|
||||
read_handler->tcp_write_queue_needs_send();
|
||||
}
|
||||
|
||||
typename Protocol::socket& socket;
|
||||
bool halt;
|
||||
ReadHandler read_handler;
|
||||
Frame::Context frame_context;
|
||||
SessionStats::Ptr stats;
|
||||
const size_t send_queue_max_size;
|
||||
const size_t free_list_max_size;
|
||||
Queue queue; // send queue
|
||||
Queue free_list; // recycled free buffers for send queue
|
||||
PacketStream pktstream;
|
||||
TransportMutateStream::Ptr mutate;
|
||||
bool raw_mode_read;
|
||||
bool raw_mode_write;
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
std::unique_ptr<Gremlin::SendRecvQueue> gremlin;
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual void recv_buffer(PacketFrom::SPtr& pfp, const size_t bytes_recvd) = 0;
|
||||
virtual void from_app_send_buffer(BufferPtr& buf) = 0;
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
// 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 UDP transport object.
|
||||
|
||||
#ifndef OPENVPN_TRANSPORT_UDPLINK_H
|
||||
#define OPENVPN_TRANSPORT_UDPLINK_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/io/io.hpp>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
#include <openvpn/transport/gremlin.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_DEBUG_UDPLINK) && OPENVPN_DEBUG_UDPLINK >= 1
|
||||
#define OPENVPN_LOG_UDPLINK_ERROR(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_UDPLINK_ERROR(x)
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_DEBUG_UDPLINK) && OPENVPN_DEBUG_UDPLINK >= 3
|
||||
#define OPENVPN_LOG_UDPLINK_VERBOSE(x) OPENVPN_LOG(x)
|
||||
#else
|
||||
#define OPENVPN_LOG_UDPLINK_VERBOSE(x)
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
namespace UDPTransport {
|
||||
|
||||
typedef openvpn_io::ip::udp::endpoint AsioEndpoint;
|
||||
|
||||
enum {
|
||||
SEND_SOCKET_HALTED=-1,
|
||||
SEND_PARTIAL=-2,
|
||||
};
|
||||
|
||||
struct PacketFrom
|
||||
{
|
||||
typedef std::unique_ptr<PacketFrom> SPtr;
|
||||
BufferAllocated buf;
|
||||
AsioEndpoint sender_endpoint;
|
||||
};
|
||||
|
||||
template <typename ReadHandler>
|
||||
class Link : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Link> Ptr;
|
||||
|
||||
Link(ReadHandler read_handler_arg,
|
||||
openvpn_io::ip::udp::socket& socket_arg,
|
||||
const Frame::Context& frame_context_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
: socket(socket_arg),
|
||||
halt(false),
|
||||
read_handler(read_handler_arg),
|
||||
frame_context(frame_context_arg),
|
||||
stats(stats_arg)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
void gremlin_config(const Gremlin::Config::Ptr& config)
|
||||
{
|
||||
if (config)
|
||||
gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, false));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Returns 0 on success, or a system error code on error.
|
||||
// May also return SEND_PARTIAL or SEND_SOCKET_HALTED.
|
||||
int send(const Buffer& buf, const AsioEndpoint* endpoint)
|
||||
{
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
{
|
||||
gremlin_send(buf, endpoint);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return do_send(buf, endpoint);
|
||||
}
|
||||
|
||||
void start(const int n_parallel)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
for (int i = 0; i < n_parallel; i++)
|
||||
queue_read(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
halt = true;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
gremlin->stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
void reset_align_adjust(const size_t align_adjust)
|
||||
{
|
||||
frame_context.reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
~Link() { stop(); }
|
||||
|
||||
private:
|
||||
void queue_read(PacketFrom *udpfrom)
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_VERBOSE("UDPLink::queue_read");
|
||||
if (!udpfrom)
|
||||
udpfrom = new PacketFrom();
|
||||
frame_context.prepare(udpfrom->buf);
|
||||
socket.async_receive_from(frame_context.mutable_buffer(udpfrom->buf),
|
||||
udpfrom->sender_endpoint,
|
||||
[self=Ptr(this), udpfrom=PacketFrom::SPtr(udpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
|
||||
{
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->handle_read(std::move(udpfrom), error, 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());
|
||||
if (!halt)
|
||||
{
|
||||
if (bytes_recvd)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_VERBOSE("UDP[" << bytes_recvd << "] from " << pfp->sender_endpoint);
|
||||
pfp->buf.set_size(bytes_recvd);
|
||||
stats->inc_stat(SessionStats::BYTES_IN, bytes_recvd);
|
||||
stats->inc_stat(SessionStats::PACKETS_IN, 1);
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
if (gremlin)
|
||||
gremlin_recv(pfp);
|
||||
else
|
||||
#endif
|
||||
read_handler->udp_read_handler(pfp);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_ERROR("UDP recv error: " << error.message());
|
||||
stats->error(Error::NETWORK_RECV_ERROR);
|
||||
}
|
||||
}
|
||||
if (!halt)
|
||||
queue_read(pfp.release()); // reuse PacketFrom object if still available
|
||||
}
|
||||
}
|
||||
|
||||
int do_send(const Buffer& buf, const AsioEndpoint* endpoint)
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
try {
|
||||
const size_t wrote = endpoint
|
||||
? socket.send_to(buf.const_buffer(), *endpoint)
|
||||
: socket.send(buf.const_buffer());
|
||||
stats->inc_stat(SessionStats::BYTES_OUT, wrote);
|
||||
stats->inc_stat(SessionStats::PACKETS_OUT, 1);
|
||||
if (wrote == buf.size())
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_ERROR("UDP partial send error");
|
||||
stats->error(Error::NETWORK_SEND_ERROR);
|
||||
return SEND_PARTIAL;
|
||||
}
|
||||
}
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_UDPLINK_ERROR("UDP send exception: " << e.what());
|
||||
stats->error(Error::NETWORK_SEND_ERROR);
|
||||
return e.code().value();
|
||||
}
|
||||
}
|
||||
else
|
||||
return SEND_SOCKET_HALTED;
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
void gremlin_send(const Buffer& buf, const AsioEndpoint* endpoint)
|
||||
{
|
||||
std::unique_ptr<AsioEndpoint> ep;
|
||||
if (endpoint)
|
||||
ep.reset(new AsioEndpoint(*endpoint));
|
||||
gremlin->send_queue([self=Ptr(this), buf=BufferAllocated(buf, 0), ep=std::move(ep)]() mutable {
|
||||
if (!self->halt)
|
||||
self->do_send(buf, ep.get());
|
||||
});
|
||||
}
|
||||
|
||||
void gremlin_recv(PacketFrom::SPtr& pfp)
|
||||
{
|
||||
gremlin->recv_queue([self=Ptr(this), pfp=std::move(pfp)]() mutable {
|
||||
if (!self->halt)
|
||||
self->read_handler->udp_read_handler(pfp);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
openvpn_io::ip::udp::socket& socket;
|
||||
bool halt;
|
||||
ReadHandler read_handler;
|
||||
Frame::Context frame_context;
|
||||
SessionStats::Ptr stats;
|
||||
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
std::unique_ptr<Gremlin::SendRecvQueue> gremlin;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user