mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 04:02:15 +02:00
Added Relay capability, a kind of proxy function similar
to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
This commit is contained in:
parent
c3dc14f3a0
commit
9c0397ebd3
@ -75,7 +75,7 @@ namespace openvpn {
|
||||
std::ostringstream os;
|
||||
os << "*** AuthCreds ***" << std::endl;
|
||||
os << "user: '" << username << "'" << std::endl;
|
||||
//os << "pass: '" << password << "'" << std::endl;
|
||||
os << "pass: (" << password.length() << " chars)" << std::endl;
|
||||
os << "peer info:" << std::endl;
|
||||
os << peer_info.render(Option::RENDER_BRACKET|Option::RENDER_NUMBER);
|
||||
return os.str();
|
||||
|
@ -50,6 +50,7 @@
|
||||
#define OPENVPN_CLIENT_CLICONNECT_H
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
@ -370,13 +371,13 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
void queue_restart(const unsigned int delay = 2)
|
||||
void queue_restart(const unsigned int delay_ms = 2000)
|
||||
{
|
||||
OPENVPN_LOG("Client terminated, restarting in " << delay << "...");
|
||||
OPENVPN_LOG("Client terminated, restarting in " << delay_ms << " ms...");
|
||||
server_poll_timer.cancel();
|
||||
interim_finalize();
|
||||
client_options->remote_reset_cache_item();
|
||||
restart_wait_timer.expires_at(Time::now() + Time::Duration::seconds(delay));
|
||||
restart_wait_timer.expires_at(Time::now() + Time::Duration::milliseconds(delay_ms));
|
||||
restart_wait_timer.async_wait([self=Ptr(this), gen=generation](const asio::error_code& error)
|
||||
{
|
||||
self->restart_wait_callback(gen, error);
|
||||
@ -504,7 +505,7 @@ namespace openvpn {
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TransportError(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TRANSPORT_ERROR);
|
||||
queue_restart(5); // use a larger timeout to allow preemption from higher levels
|
||||
queue_restart(5000); // use a larger timeout to allow preemption from higher levels
|
||||
}
|
||||
break;
|
||||
case Error::TUN_ERROR:
|
||||
@ -512,7 +513,24 @@ namespace openvpn {
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::TunError(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::TUN_ERROR);
|
||||
queue_restart(5);
|
||||
queue_restart(5000);
|
||||
}
|
||||
break;
|
||||
case Error::RELAY:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Relay();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::RELAY);
|
||||
transport_factory_relay = client->transport_factory_relay();
|
||||
queue_restart(0);
|
||||
}
|
||||
break;
|
||||
case Error::RELAY_ERROR:
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::RelayError(client->fatal_reason());
|
||||
client_options->events().add_event(std::move(ev));
|
||||
client_options->stats().error(Error::RELAY_ERROR);
|
||||
stop();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -531,7 +549,7 @@ namespace openvpn {
|
||||
client->stop(false);
|
||||
interim_finalize();
|
||||
}
|
||||
if (generation > 1)
|
||||
if (generation > 1 && !transport_factory_relay)
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Reconnecting();
|
||||
client_options->events().add_event(std::move(ev));
|
||||
@ -539,10 +557,19 @@ namespace openvpn {
|
||||
if (!(client && client->reached_connected_state()))
|
||||
client_options->next();
|
||||
}
|
||||
Client::Config::Ptr cli_config = client_options->client_config(); // client_config in cliopt.hpp
|
||||
|
||||
// client_config in cliopt.hpp
|
||||
Client::Config::Ptr cli_config = client_options->client_config(!transport_factory_relay);
|
||||
client.reset(new Client(io_context, *cli_config, this)); // build ClientProto::Session from cliproto.hpp
|
||||
client_finalized = false;
|
||||
|
||||
// relay?
|
||||
if (transport_factory_relay)
|
||||
{
|
||||
client->transport_factory_override(std::move(transport_factory_relay));
|
||||
transport_factory_relay.reset();
|
||||
}
|
||||
|
||||
restart_wait_timer.cancel();
|
||||
if (client_options->server_poll_timeout_enabled())
|
||||
{
|
||||
@ -588,6 +615,7 @@ namespace openvpn {
|
||||
asio::io_context& io_context;
|
||||
ClientOptions::Ptr client_options;
|
||||
Client::Ptr client;
|
||||
TransportClientFactory::Ptr transport_factory_relay;
|
||||
AsioTimer server_poll_timer;
|
||||
AsioTimer restart_wait_timer;
|
||||
AsioTimer conn_timer;
|
||||
|
@ -52,6 +52,7 @@ namespace openvpn {
|
||||
INFO,
|
||||
PAUSE,
|
||||
RESUME,
|
||||
RELAY,
|
||||
|
||||
// start of nonfatal errors, must be marked by NONFATAL_ERROR_START below
|
||||
TRANSPORT_ERROR,
|
||||
@ -73,6 +74,7 @@ namespace openvpn {
|
||||
TUN_IFACE_DISABLED,
|
||||
EPKI_ERROR, // EPKI refers to External PKI errors, i.e. errors in accessing external
|
||||
EPKI_INVALID_ALIAS, // certificates or keys.
|
||||
RELAY_ERROR,
|
||||
|
||||
N_TYPES
|
||||
};
|
||||
@ -99,6 +101,7 @@ namespace openvpn {
|
||||
"INFO",
|
||||
"PAUSE",
|
||||
"RESUME",
|
||||
"RELAY",
|
||||
|
||||
// nonfatal errors
|
||||
"TRANSPORT_ERROR",
|
||||
@ -120,6 +123,7 @@ namespace openvpn {
|
||||
"TUN_IFACE_DISABLED",
|
||||
"EPKI_ERROR",
|
||||
"EPKI_INVALID_ALIAS",
|
||||
"RELAY_ERROR",
|
||||
};
|
||||
|
||||
static_assert(N_TYPES == array_size(names), "event names array inconsistency");
|
||||
@ -220,6 +224,11 @@ namespace openvpn {
|
||||
Resume() : Base(RESUME) {}
|
||||
};
|
||||
|
||||
struct Relay : public Base
|
||||
{
|
||||
Relay() : Base(RELAY) {}
|
||||
};
|
||||
|
||||
struct Disconnected : public Base
|
||||
{
|
||||
Disconnected() : Base(DISCONNECTED) {}
|
||||
@ -316,6 +325,11 @@ namespace openvpn {
|
||||
ClientRestart(std::string reason) : ReasonBase(CLIENT_RESTART, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct RelayError : public ReasonBase
|
||||
{
|
||||
RelayError(std::string reason) : ReasonBase(RELAY_ERROR, std::move(reason)) {}
|
||||
};
|
||||
|
||||
struct DynamicChallenge : public ReasonBase
|
||||
{
|
||||
DynamicChallenge(std::string reason) : ReasonBase(DYNAMIC_CHALLENGE, std::move(reason)) {}
|
||||
|
@ -209,37 +209,10 @@ namespace openvpn {
|
||||
// route-nopull
|
||||
pushed_options_filter.reset(new PushedOptionsFilter(opt.exists("route-nopull")));
|
||||
|
||||
// client SSL config
|
||||
SSLLib::SSLAPI::Config::Ptr cc(new SSLLib::SSLAPI::Config());
|
||||
cc->set_external_pki_callback(config.external_pki);
|
||||
cc->set_frame(frame);
|
||||
cc->set_flags(SSLConst::LOG_VERIFY_STATUS);
|
||||
cc->set_debug_level(config.ssl_debug_level);
|
||||
cc->set_rng(rng);
|
||||
cc->set_local_cert_enabled(pcc.clientCertEnabled() && !config.disable_client_cert);
|
||||
cc->set_private_key_password(config.private_key_password);
|
||||
cc->set_force_aes_cbc_ciphersuites(config.force_aes_cbc_ciphersuites);
|
||||
cc->load(opt, SSLConfigAPI::LF_PARSE_MODE);
|
||||
cc->set_tls_version_min_override(config.tls_version_min_override);
|
||||
if (!cc->get_mode().is_client())
|
||||
throw option_error("only client configuration supported");
|
||||
|
||||
// client ProtoContext config
|
||||
cp.reset(new Client::ProtoConfig());
|
||||
cp->dc.set_factory(new CryptoDCSelect<SSLLib::CryptoAPI>(frame, cli_stats, prng));
|
||||
cp->dc_deferred = true; // defer data channel setup until after options pull
|
||||
cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<SSLLib::CryptoAPI>());
|
||||
cp->tlsprf_factory.reset(new CryptoTLSPRFFactory<SSLLib::CryptoAPI>());
|
||||
cp->ssl_factory = cc->new_factory();
|
||||
cp->load(opt, *proto_context_options, config.default_key_direction, false);
|
||||
cp->set_xmit_creds(!autologin || pcc.hasEmbeddedPassword() || autologin_sessions);
|
||||
cp->gui_version = config.gui_version;
|
||||
cp->force_aes_cbc_ciphersuites = config.force_aes_cbc_ciphersuites; // also used to disable proto V2
|
||||
cp->extra_peer_info = build_peer_info(config, pcc, autologin_sessions);
|
||||
cp->frame = frame;
|
||||
cp->now = &now_;
|
||||
cp->rng = rng;
|
||||
cp->prng = prng;
|
||||
// OpenVPN Protocol context (including SSL)
|
||||
cp_main = proto_config(opt, config, pcc, false);
|
||||
cp_relay = proto_config(opt, config, pcc, true); // may be null
|
||||
layer = cp_main->layer;
|
||||
|
||||
#ifdef PRIVATE_TUNNEL_PROXY
|
||||
if (config.alt_proxy && !dco)
|
||||
@ -307,7 +280,7 @@ namespace openvpn {
|
||||
if (dco)
|
||||
{
|
||||
DCO::TunConfig tunconf;
|
||||
tunconf.tun_prop.layer = cp->layer;
|
||||
tunconf.tun_prop.layer = layer;
|
||||
tunconf.tun_prop.session_name = session_name;
|
||||
if (tun_mtu)
|
||||
tunconf.tun_prop.mtu = tun_mtu;
|
||||
@ -359,7 +332,7 @@ namespace openvpn {
|
||||
#elif defined(OPENVPN_PLATFORM_LINUX) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunLinux::ClientConfig::Ptr tunconf = TunLinux::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.layer = layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
@ -373,7 +346,7 @@ namespace openvpn {
|
||||
#elif defined(OPENVPN_PLATFORM_MAC) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunMac::ClientConfig::Ptr tunconf = TunMac::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.layer = layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
@ -392,7 +365,7 @@ namespace openvpn {
|
||||
#elif defined(OPENVPN_PLATFORM_WIN) && !defined(OPENVPN_FORCE_TUN_NULL)
|
||||
{
|
||||
TunWin::ClientConfig::Ptr tunconf = TunWin::ClientConfig::new_obj();
|
||||
tunconf->tun_prop.layer = cp->layer;
|
||||
tunconf->tun_prop.layer = layer;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
if (tun_mtu)
|
||||
@ -418,7 +391,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// verify that tun implementation can handle OSI layer declared by config
|
||||
if (cp->layer == Layer(Layer::OSI_LAYER_2) && !tun_factory->layer_2_supported())
|
||||
if (layer == Layer(Layer::OSI_LAYER_2) && !tun_factory->layer_2_supported())
|
||||
throw ErrorCode(Error::TAP_NOT_SUPPORTED, true, "OSI layer 2 tunnels are not currently supported");
|
||||
|
||||
// server-poll-timeout
|
||||
@ -539,13 +512,13 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
Client::Config::Ptr client_config()
|
||||
Client::Config::Ptr client_config(const bool relay_mode)
|
||||
{
|
||||
Client::Config::Ptr cli_config = new Client::Config;
|
||||
|
||||
// Copy ProtoConfig so that modifications due to server push will
|
||||
// not persist across client instantiations.
|
||||
cli_config->proto_context_config.reset(new Client::ProtoConfig(*cp));
|
||||
cli_config->proto_context_config.reset(new Client::ProtoConfig(proto_config_cached(relay_mode)));
|
||||
|
||||
cli_config->proto_context_options = proto_context_options;
|
||||
cli_config->push_base = push_base;
|
||||
@ -626,14 +599,69 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
private:
|
||||
Client::ProtoConfig& proto_config_cached(const bool relay_mode)
|
||||
{
|
||||
if (relay_mode && cp_relay)
|
||||
return *cp_relay;
|
||||
else
|
||||
return *cp_main;
|
||||
}
|
||||
|
||||
Client::ProtoConfig::Ptr proto_config(const OptionList& opt,
|
||||
const Config& config,
|
||||
const ParseClientConfig& pcc,
|
||||
const bool relay_mode)
|
||||
{
|
||||
// relay mode is null unless one of the below directives is defined
|
||||
if (relay_mode && !opt.exists("relay-mode"))
|
||||
return Client::ProtoConfig::Ptr();
|
||||
|
||||
// load flags
|
||||
unsigned int lflags = SSLConfigAPI::LF_PARSE_MODE;
|
||||
if (relay_mode)
|
||||
lflags |= SSLConfigAPI::LF_RELAY_MODE;
|
||||
|
||||
// client SSL config
|
||||
SSLLib::SSLAPI::Config::Ptr cc(new SSLLib::SSLAPI::Config());
|
||||
cc->set_external_pki_callback(config.external_pki);
|
||||
cc->set_frame(frame);
|
||||
cc->set_flags(SSLConst::LOG_VERIFY_STATUS);
|
||||
cc->set_debug_level(config.ssl_debug_level);
|
||||
cc->set_rng(rng);
|
||||
cc->set_local_cert_enabled(pcc.clientCertEnabled() && !config.disable_client_cert);
|
||||
cc->set_private_key_password(config.private_key_password);
|
||||
cc->set_force_aes_cbc_ciphersuites(config.force_aes_cbc_ciphersuites);
|
||||
cc->load(opt, lflags);
|
||||
cc->set_tls_version_min_override(config.tls_version_min_override);
|
||||
if (!cc->get_mode().is_client())
|
||||
throw option_error("only client configuration supported");
|
||||
|
||||
// client ProtoContext config
|
||||
Client::ProtoConfig::Ptr cp(new Client::ProtoConfig());
|
||||
cp->relay_mode = relay_mode;
|
||||
cp->dc.set_factory(new CryptoDCSelect<SSLLib::CryptoAPI>(frame, cli_stats, prng));
|
||||
cp->dc_deferred = true; // defer data channel setup until after options pull
|
||||
cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<SSLLib::CryptoAPI>());
|
||||
cp->tlsprf_factory.reset(new CryptoTLSPRFFactory<SSLLib::CryptoAPI>());
|
||||
cp->ssl_factory = cc->new_factory();
|
||||
cp->load(opt, *proto_context_options, config.default_key_direction, false);
|
||||
cp->set_xmit_creds(!autologin || pcc.hasEmbeddedPassword() || autologin_sessions);
|
||||
cp->gui_version = config.gui_version;
|
||||
cp->force_aes_cbc_ciphersuites = config.force_aes_cbc_ciphersuites; // also used to disable proto V2
|
||||
cp->extra_peer_info = build_peer_info(config, pcc, autologin_sessions);
|
||||
cp->frame = frame;
|
||||
cp->now = &now_;
|
||||
cp->rng = rng;
|
||||
cp->prng = prng;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
std::string load_transport_config()
|
||||
{
|
||||
// get current transport protocol
|
||||
const Protocol& transport_protocol = remote_list->current_transport_protocol();
|
||||
|
||||
// set transport protocol in Client::ProtoConfig
|
||||
cp->set_protocol(transport_protocol);
|
||||
|
||||
// If we are connecting over a proxy, and TCP protocol is required, but current
|
||||
// transport protocol is NOT TCP, we will throw an internal error because this
|
||||
// should have been caught earlier in RemoteList::handle_proto_override.
|
||||
@ -720,8 +748,9 @@ namespace openvpn {
|
||||
RandomAPI::Ptr rng;
|
||||
RandomAPI::Ptr prng;
|
||||
Frame::Ptr frame;
|
||||
SSLLib::SSLAPI::Config cc;
|
||||
Client::ProtoConfig::Ptr cp;
|
||||
Layer layer;
|
||||
Client::ProtoConfig::Ptr cp_main;
|
||||
Client::ProtoConfig::Ptr cp_relay;
|
||||
RemoteList::Ptr remote_list;
|
||||
bool server_addr_float;
|
||||
TransportClientFactory::Ptr transport_factory;
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <openvpn/common/base64.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/transport/client/relay.hpp>
|
||||
#include <openvpn/options/continuation.hpp>
|
||||
#include <openvpn/options/sanitize.hpp>
|
||||
#include <openvpn/client/clievent.hpp>
|
||||
@ -101,6 +102,7 @@ namespace openvpn {
|
||||
OPENVPN_SIMPLE_EXCEPTION(session_invalidated);
|
||||
OPENVPN_SIMPLE_EXCEPTION(authentication_failed);
|
||||
OPENVPN_SIMPLE_EXCEPTION(inactive_timer_expired);
|
||||
OPENVPN_SIMPLE_EXCEPTION(relay_event);
|
||||
|
||||
OPENVPN_EXCEPTION(proxy_exception);
|
||||
|
||||
@ -181,12 +183,27 @@ namespace openvpn {
|
||||
housekeeping_schedule.init(Time::Duration::binary_ms(512), Time::Duration::binary_ms(1024));
|
||||
|
||||
// initialize transport-layer packet handler
|
||||
transport = transport_factory->new_transport_client_obj(io_context, *this);
|
||||
transport = transport_factory->new_transport_client_obj(io_context, this);
|
||||
transport_has_send_queue = transport->transport_has_send_queue();
|
||||
transport->transport_start();
|
||||
if (transport_factory->is_relay())
|
||||
transport_connecting();
|
||||
else
|
||||
transport->transport_start();
|
||||
}
|
||||
}
|
||||
|
||||
TransportClientFactory::Ptr transport_factory_relay()
|
||||
{
|
||||
TransportClient::Ptr tc(new TransportRelayFactory::TransportClientNull(transport.get()));
|
||||
tc.swap(transport);
|
||||
return new TransportRelayFactory(io_context, std::move(tc), this);
|
||||
}
|
||||
|
||||
void transport_factory_override(TransportClientFactory::Ptr factory)
|
||||
{
|
||||
transport_factory = std::move(factory);
|
||||
}
|
||||
|
||||
void send_explicit_exit_notify()
|
||||
{
|
||||
if (!halt)
|
||||
@ -425,6 +442,7 @@ namespace openvpn {
|
||||
{
|
||||
try {
|
||||
OPENVPN_LOG("Connecting to " << server_endpoint_render());
|
||||
Base::set_protocol(transport->transport_protocol());
|
||||
Base::start();
|
||||
Base::flush(true);
|
||||
set_housekeeping_timer();
|
||||
@ -526,6 +544,13 @@ namespace openvpn {
|
||||
// show options
|
||||
OPENVPN_LOG("OPTIONS:" << std::endl << render_options_sanitized(received_options, Option::RENDER_PASS_FMT|Option::RENDER_NUMBER|Option::RENDER_BRACKET));
|
||||
|
||||
// relay servers are not allowed to establish a tunnel with us
|
||||
if (Base::conf().relay_mode)
|
||||
{
|
||||
tun_error(Error::RELAY_ERROR, "tunnel not permitted to relay server");
|
||||
return;
|
||||
}
|
||||
|
||||
// process "echo" directives
|
||||
if (echo)
|
||||
process_echo(received_options);
|
||||
@ -616,6 +641,26 @@ namespace openvpn {
|
||||
else
|
||||
cli_events->add_event(std::move(ev));
|
||||
}
|
||||
else if (msg == "RELAY")
|
||||
{
|
||||
if (Base::conf().relay_mode)
|
||||
{
|
||||
fatal_ = Error::RELAY;
|
||||
fatal_reason_ = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
fatal_ = Error::RELAY_ERROR;
|
||||
fatal_reason_ = "not in relay mode";
|
||||
}
|
||||
if (notify_callback)
|
||||
{
|
||||
OPENVPN_LOG(Error::name(fatal_) << ' ' << fatal_reason_);
|
||||
stop(true);
|
||||
}
|
||||
else
|
||||
throw relay_event();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void tun_pre_tun_config()
|
||||
@ -674,7 +719,8 @@ namespace openvpn {
|
||||
// proto base class calls here to get auth credentials
|
||||
virtual void client_auth(Buffer& buf)
|
||||
{
|
||||
if (creds)
|
||||
// we never send creds to a relay server
|
||||
if (creds && !Base::conf().relay_mode)
|
||||
{
|
||||
OPENVPN_LOG("Creds: " << creds->auth_info());
|
||||
Base::write_auth_string(creds->get_username(), buf);
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
@ -71,6 +73,13 @@ namespace openvpn {
|
||||
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
|
||||
throw Exception("error setting FD_CLOEXEC on file-descriptor/socket");
|
||||
}
|
||||
|
||||
// set non-block mode on socket
|
||||
static inline void set_nonblock(const int fd)
|
||||
{
|
||||
if (::fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
||||
throw Exception("error setting socket to non-blocking mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,8 @@ namespace openvpn {
|
||||
AUTH_FAILED, // general authentication failure
|
||||
CLIENT_HALT, // HALT message from server received
|
||||
CLIENT_RESTART, // RESTART message from server received
|
||||
RELAY, // RELAY message from server received
|
||||
RELAY_ERROR, // RELAY error
|
||||
N_PAUSE, // Number of transitions to Pause state
|
||||
N_RECONNECT, // Number of reconnections
|
||||
N_KEY_LIMIT_RENEG, // Number of renegotiations triggered by per-key limits such as data or packet limits
|
||||
@ -148,6 +150,8 @@ namespace openvpn {
|
||||
"AUTH_FAILED",
|
||||
"CLIENT_HALT",
|
||||
"CLIENT_RESTART",
|
||||
"RELAY",
|
||||
"RELAY_ERROR",
|
||||
"N_PAUSE",
|
||||
"N_RECONNECT",
|
||||
"N_KEY_LIMIT_RENEG",
|
||||
|
@ -263,7 +263,9 @@ namespace openvpn {
|
||||
|
||||
// ca
|
||||
{
|
||||
const std::string ca_txt = opt.cat("ca");
|
||||
std::string ca_txt = opt.cat("ca");
|
||||
if (lflags & LF_RELAY_MODE)
|
||||
ca_txt += opt.cat("relay-extra-ca");
|
||||
load_ca(ca_txt, true);
|
||||
}
|
||||
|
||||
@ -292,16 +294,21 @@ namespace openvpn {
|
||||
load_dh(dh_txt);
|
||||
}
|
||||
|
||||
// relay mode
|
||||
std::string relay_prefix;
|
||||
if (lflags & LF_RELAY_MODE)
|
||||
relay_prefix = "relay-";
|
||||
|
||||
// ns-cert-type
|
||||
ns_cert_type = NSCert::ns_cert_type(opt);
|
||||
ns_cert_type = NSCert::ns_cert_type(opt, relay_prefix);
|
||||
|
||||
// parse remote-cert-x options
|
||||
KUParse::remote_cert_tls(opt, ku, eku);
|
||||
KUParse::remote_cert_ku(opt, ku);
|
||||
KUParse::remote_cert_eku(opt, eku);
|
||||
KUParse::remote_cert_tls(opt, relay_prefix, ku, eku);
|
||||
KUParse::remote_cert_ku(opt, relay_prefix, ku);
|
||||
KUParse::remote_cert_eku(opt, relay_prefix, eku);
|
||||
|
||||
// parse tls-remote
|
||||
tls_remote = opt.get_optional("tls-remote", 1, 256);
|
||||
tls_remote = opt.get_optional(relay_prefix + "tls-remote", 1, 256);
|
||||
|
||||
// Parse tls-version-min option.
|
||||
// Assume that presence of SSL_OP_NO_TLSvX macro indicates
|
||||
@ -314,7 +321,7 @@ namespace openvpn {
|
||||
# else
|
||||
const TLSVersion::Type maxver = TLSVersion::V1_0;
|
||||
# endif
|
||||
tls_version_min = TLSVersion::parse_tls_version_min(opt, maxver);
|
||||
tls_version_min = TLSVersion::parse_tls_version_min(opt, relay_prefix, maxver);
|
||||
}
|
||||
|
||||
// unsupported cert checkers
|
||||
|
@ -405,6 +405,15 @@ namespace openvpn {
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
case 'r':
|
||||
if (d == "relay-extra-ca")
|
||||
return true;
|
||||
if (d == "relay-tls-auth")
|
||||
{
|
||||
flags |= F_MAY_INCLUDE_KEY_DIRECTION;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 't':
|
||||
if (d == "tls-auth")
|
||||
{
|
||||
|
@ -324,7 +324,9 @@ namespace openvpn {
|
||||
|
||||
// ca
|
||||
{
|
||||
const std::string ca_txt = opt.cat("ca");
|
||||
std::string ca_txt = opt.cat("ca");
|
||||
if (lflags & LF_RELAY_MODE)
|
||||
ca_txt += opt.cat("relay-extra-ca");
|
||||
load_ca(ca_txt, true);
|
||||
}
|
||||
|
||||
@ -360,16 +362,21 @@ namespace openvpn {
|
||||
load_dh(dh_txt);
|
||||
}
|
||||
|
||||
// relay mode
|
||||
std::string relay_prefix;
|
||||
if (lflags & LF_RELAY_MODE)
|
||||
relay_prefix = "relay-";
|
||||
|
||||
// parse ns-cert-type
|
||||
ns_cert_type = NSCert::ns_cert_type(opt);
|
||||
ns_cert_type = NSCert::ns_cert_type(opt, relay_prefix);
|
||||
|
||||
// parse remote-cert-x options
|
||||
KUParse::remote_cert_tls(opt, ku, eku);
|
||||
KUParse::remote_cert_ku(opt, ku);
|
||||
KUParse::remote_cert_eku(opt, eku);
|
||||
KUParse::remote_cert_tls(opt, relay_prefix, ku, eku);
|
||||
KUParse::remote_cert_ku(opt, relay_prefix, ku);
|
||||
KUParse::remote_cert_eku(opt, relay_prefix, eku);
|
||||
|
||||
// parse tls-remote
|
||||
tls_remote = opt.get_optional("tls-remote", 1, 256);
|
||||
tls_remote = opt.get_optional(relay_prefix + "tls-remote", 1, 256);
|
||||
|
||||
// parse tls-version-min option
|
||||
{
|
||||
@ -380,7 +387,7 @@ namespace openvpn {
|
||||
# else
|
||||
const TLSVersion::Type maxver = TLSVersion::V1_0;
|
||||
# endif
|
||||
tls_version_min = TLSVersion::parse_tls_version_min(opt, maxver);
|
||||
tls_version_min = TLSVersion::parse_tls_version_min(opt, relay_prefix, maxver);
|
||||
}
|
||||
|
||||
// unsupported cert verification options
|
||||
|
@ -103,11 +103,14 @@ namespace openvpn {
|
||||
|
||||
|
||||
// send control channel message
|
||||
virtual void post_info(BufferPtr&& info) = 0;
|
||||
virtual void post_cc_msg(BufferPtr&& msg) = 0;
|
||||
|
||||
// set fwmark value in client instance
|
||||
virtual void set_fwmark(const unsigned int fwmark) = 0;
|
||||
|
||||
// set up relay to target
|
||||
virtual void relay(const IP::Addr& target, const int port) = 0;
|
||||
|
||||
// get client bandwidth stats
|
||||
virtual PeerStats stats_poll() = 0;
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#ifndef OPENVPN_SERVER_SERVPROTO_H
|
||||
#define OPENVPN_SERVER_SERVPROTO_H
|
||||
|
||||
#include <memory>
|
||||
#include <utility> // for std::move
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
@ -196,6 +197,8 @@ namespace openvpn {
|
||||
virtual bool transport_recv(BufferAllocated& buf)
|
||||
{
|
||||
bool ret = false;
|
||||
if (!Base::primary_defined())
|
||||
return false;
|
||||
try {
|
||||
OPENVPN_LOG_SERVPROTO("Transport RECV[" << buf.size() << "] " << client_endpoint_render() << ' ' << Base::dump_packet(buf));
|
||||
|
||||
@ -287,9 +290,6 @@ namespace openvpn {
|
||||
TunClientInstanceFactory::Ptr tun_factory_arg)
|
||||
: Base(factory.clone_proto_config(), factory.stats),
|
||||
io_context(io_context_arg),
|
||||
halt(false),
|
||||
did_push(false),
|
||||
did_client_halt_restart(false),
|
||||
housekeeping_timer(io_context_arg),
|
||||
disconnect_at(Time::infinite()),
|
||||
stats(factory.stats),
|
||||
@ -368,13 +368,38 @@ namespace openvpn {
|
||||
TunLink::send->set_fwmark(fwmark);
|
||||
}
|
||||
|
||||
virtual void relay(const IP::Addr& target, const int port)
|
||||
{
|
||||
Base::update_now();
|
||||
|
||||
if (TunLink::send && !relay_transition)
|
||||
{
|
||||
relay_transition = true;
|
||||
TunLink::send->relay(target, port);
|
||||
disconnect_in(Time::Duration::seconds(10)); // not a real disconnect, just complete transition to relay
|
||||
}
|
||||
|
||||
if (Base::primary_defined())
|
||||
{
|
||||
BufferPtr buf(new BufferAllocated(64, 0));
|
||||
buf_append_string(*buf, "RELAY");
|
||||
buf->null_terminate();
|
||||
Base::control_send(std::move(buf));
|
||||
Base::flush(true);
|
||||
}
|
||||
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void push_reply(std::vector<BufferPtr>&& push_msgs,
|
||||
const std::vector<IP::Route>& rtvec,
|
||||
const unsigned int initial_fwmark)
|
||||
{
|
||||
if (halt)
|
||||
if (halt || relay_transition || !Base::primary_defined())
|
||||
return;
|
||||
|
||||
Base::update_now();
|
||||
|
||||
if (get_tun())
|
||||
{
|
||||
Base::init_data_channel();
|
||||
@ -469,20 +494,24 @@ namespace openvpn {
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
}
|
||||
|
||||
buf->null_terminate();
|
||||
Base::control_send(std::move(buf));
|
||||
Base::flush(true);
|
||||
if (Base::primary_defined())
|
||||
{
|
||||
buf->null_terminate();
|
||||
Base::control_send(std::move(buf));
|
||||
Base::flush(true);
|
||||
}
|
||||
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
|
||||
virtual void post_info(BufferPtr&& info)
|
||||
virtual void post_cc_msg(BufferPtr&& msg)
|
||||
{
|
||||
if (halt)
|
||||
if (halt || !Base::primary_defined())
|
||||
return;
|
||||
|
||||
Base::update_now();
|
||||
info->null_terminate();
|
||||
Base::control_send(std::move(info));
|
||||
msg->null_terminate();
|
||||
Base::control_send(std::move(msg));
|
||||
Base::flush(true);
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
@ -529,6 +558,8 @@ namespace openvpn {
|
||||
return bool(TunLink::send);
|
||||
}
|
||||
|
||||
// caller must ensure that update_now() was called before
|
||||
// and set_housekeeping_timer() called after this method
|
||||
void disconnect_in(const Time::Duration& dur)
|
||||
{
|
||||
disconnect_at = now() + dur;
|
||||
@ -547,7 +578,12 @@ namespace openvpn {
|
||||
if (Base::invalidated())
|
||||
invalidation_error(Base::invalidation_reason());
|
||||
else if (now() >= disconnect_at)
|
||||
error("disconnect triggered");
|
||||
{
|
||||
if (relay_transition && !did_client_halt_restart)
|
||||
Base::pre_destroy();
|
||||
else
|
||||
error("disconnect triggered");
|
||||
}
|
||||
else
|
||||
set_housekeeping_timer();
|
||||
}
|
||||
@ -620,9 +656,11 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
asio::io_context& io_context;
|
||||
bool halt;
|
||||
bool did_push;
|
||||
bool did_client_halt_restart;
|
||||
|
||||
bool halt = false;
|
||||
bool did_push = false;
|
||||
bool did_client_halt_restart = false;
|
||||
bool relay_transition = false;
|
||||
|
||||
PeerAddr::Ptr peer_addr;
|
||||
|
||||
|
@ -65,10 +65,13 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
inline void remote_cert_tls(const OptionList& opt, std::vector<unsigned int>& ku, std::string& eku)
|
||||
inline void remote_cert_tls(const OptionList& opt,
|
||||
const std::string& relay_prefix,
|
||||
std::vector<unsigned int>& ku,
|
||||
std::string& eku)
|
||||
{
|
||||
TLSWebType wt = TLS_WEB_NONE;
|
||||
const Option* o = opt.get_ptr("remote-cert-tls");
|
||||
const Option* o = opt.get_ptr(relay_prefix + "remote-cert-tls");
|
||||
if (o)
|
||||
{
|
||||
const std::string ct = o->get_optional(1, 16);
|
||||
@ -82,11 +85,13 @@ namespace openvpn {
|
||||
remote_cert_tls(wt, ku, eku);
|
||||
}
|
||||
|
||||
inline void remote_cert_ku(const OptionList& opt, std::vector<unsigned int>& ku)
|
||||
inline void remote_cert_ku(const OptionList& opt,
|
||||
const std::string& relay_prefix,
|
||||
std::vector<unsigned int>& ku)
|
||||
{
|
||||
ku.clear();
|
||||
|
||||
const Option* o = opt.get_ptr("remote-cert-ku");
|
||||
const Option* o = opt.get_ptr(relay_prefix + "remote-cert-ku");
|
||||
if (o)
|
||||
{
|
||||
if (o->empty())
|
||||
@ -107,11 +112,13 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
inline void remote_cert_eku(const OptionList& opt, std::string& eku)
|
||||
inline void remote_cert_eku(const OptionList& opt,
|
||||
const std::string& relay_prefix,
|
||||
std::string& eku)
|
||||
{
|
||||
eku = "";
|
||||
|
||||
const Option* o = opt.get_ptr("remote-cert-eku");
|
||||
const Option* o = opt.get_ptr(relay_prefix + "remote-cert-eku");
|
||||
if (o)
|
||||
eku = o->get(1, 256);
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ namespace openvpn {
|
||||
SERVER
|
||||
};
|
||||
|
||||
inline Type ns_cert_type(const OptionList& opt) {
|
||||
const Option* o = opt.get_ptr("ns-cert-type");
|
||||
inline Type ns_cert_type(const OptionList& opt, const std::string& relay_prefix) {
|
||||
const Option* o = opt.get_ptr(relay_prefix + "ns-cert-type");
|
||||
if (o)
|
||||
{
|
||||
const std::string ct = o->get_optional(1, 16);
|
||||
|
@ -270,6 +270,10 @@ namespace openvpn {
|
||||
// IV and ProtoSessionID generation.
|
||||
RandomAPI::Ptr prng;
|
||||
|
||||
// If relay mode is enabled, connect to a special OpenVPN
|
||||
// server that acts as a relay/proxy to a second server.
|
||||
bool relay_mode = false;
|
||||
|
||||
// defer data channel initialization until after client options pull
|
||||
bool dc_deferred = false;
|
||||
|
||||
@ -396,12 +400,12 @@ namespace openvpn {
|
||||
|
||||
// tls-auth
|
||||
{
|
||||
const Option *o = opt.get_ptr("tls-auth");
|
||||
const Option *o = opt.get_ptr(relay_prefix("tls-auth"));
|
||||
if (o)
|
||||
{
|
||||
tls_auth_key.parse(o->get(1, 0));
|
||||
|
||||
const Option *tad = opt.get_ptr("tls-auth-digest");
|
||||
const Option *tad = opt.get_ptr(relay_prefix("tls-auth-digest"));
|
||||
if (tad)
|
||||
digest = CryptoAlgs::lookup(tad->get(1, 128));
|
||||
if (digest != CryptoAlgs::NONE)
|
||||
@ -414,7 +418,7 @@ namespace openvpn {
|
||||
{
|
||||
if (key_direction >= -1 && key_direction <= 1)
|
||||
{
|
||||
const Option *o = opt.get_ptr("key-direction");
|
||||
const Option *o = opt.get_ptr(relay_prefix("key-direction"));
|
||||
if (o)
|
||||
{
|
||||
const std::string& dir = o->get(1, 16);
|
||||
@ -621,12 +625,6 @@ namespace openvpn {
|
||||
return tls_auth_key.defined() && tls_auth_context;
|
||||
}
|
||||
|
||||
void validate_complete() const
|
||||
{
|
||||
if (!protocol.defined())
|
||||
throw proto_option_error("transport protocol undefined");
|
||||
}
|
||||
|
||||
// generate a string summarizing options that will be
|
||||
// transmitted to peer for options consistency check
|
||||
std::string options_string()
|
||||
@ -694,6 +692,8 @@ namespace openvpn {
|
||||
out << extra_peer_info->to_string();
|
||||
if (is_bs64_cipher(dc.cipher()))
|
||||
out << "IV_BS64DL=1\n"; // indicate support for data limits when using 64-bit block-size ciphers, version 1 (CVE-2016-6329)
|
||||
if (relay_mode)
|
||||
out << "IV_RELAY=1\n";
|
||||
const std::string ret = out.str();
|
||||
OPENVPN_LOG_PROTO("Peer Info:" << std::endl << ret);
|
||||
return ret;
|
||||
@ -757,6 +757,15 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string relay_prefix(const char *optname) const
|
||||
{
|
||||
std::string ret;
|
||||
if (relay_mode)
|
||||
ret = "relay-";
|
||||
ret += optname;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
// Used to describe an incoming network packet
|
||||
@ -840,7 +849,7 @@ namespace openvpn {
|
||||
// examine key ID
|
||||
{
|
||||
const unsigned int kid = key_id_extract(op);
|
||||
if (kid == proto.primary->key_id())
|
||||
if (proto.primary && kid == proto.primary->key_id())
|
||||
flags |= DEFINED;
|
||||
else if (proto.secondary && kid == proto.secondary->key_id())
|
||||
flags |= (DEFINED | SECONDARY);
|
||||
@ -1221,9 +1230,11 @@ namespace openvpn {
|
||||
crypto_flags(0),
|
||||
dirty(0),
|
||||
key_limit_renegotiation_fired(false),
|
||||
is_reliable(p.config->protocol.is_reliable()),
|
||||
tlsprf(p.config->tlsprf_factory->new_obj(p.is_server()))
|
||||
{
|
||||
// reliable protocol?
|
||||
set_protocol(proto.config->protocol);
|
||||
|
||||
// get key_id from parent
|
||||
key_id_ = proto.next_key_id();
|
||||
|
||||
@ -1240,6 +1251,11 @@ namespace openvpn {
|
||||
set_event(KEV_NONE, KEV_NEGOTIATE, construct_time + proto.config->handshake_window);
|
||||
}
|
||||
|
||||
void set_protocol(const Protocol& p)
|
||||
{
|
||||
is_reliable = p.is_reliable(); // cache is_reliable state locally
|
||||
}
|
||||
|
||||
// need to call only on the initiator side of the connection
|
||||
void start()
|
||||
{
|
||||
@ -2408,9 +2424,6 @@ namespace openvpn {
|
||||
{
|
||||
const Config& c = *config;
|
||||
|
||||
// validate options
|
||||
c.validate_complete();
|
||||
|
||||
// defer data channel initialization until after client options pull?
|
||||
dc_deferred = c.dc_deferred;
|
||||
|
||||
@ -2463,6 +2476,15 @@ namespace openvpn {
|
||||
update_last_sent(); // set timer for initial keepalive send
|
||||
}
|
||||
|
||||
void set_protocol(const Protocol& p)
|
||||
{
|
||||
config->set_protocol(p);
|
||||
if (primary)
|
||||
primary->set_protocol(p);
|
||||
if (secondary)
|
||||
secondary->set_protocol(p);
|
||||
}
|
||||
|
||||
// Free up space when parent object has been halted but
|
||||
// object destruction is not immediately scheduled.
|
||||
void pre_destroy()
|
||||
@ -2470,6 +2492,12 @@ namespace openvpn {
|
||||
reset_all();
|
||||
}
|
||||
|
||||
// Is primary key defined
|
||||
bool primary_defined()
|
||||
{
|
||||
return bool(primary);
|
||||
}
|
||||
|
||||
virtual ~ProtoContext() {}
|
||||
|
||||
// return the PacketType of an incoming network packet
|
||||
@ -2481,6 +2509,8 @@ namespace openvpn {
|
||||
// start protocol negotiation
|
||||
void start()
|
||||
{
|
||||
if (!primary)
|
||||
throw proto_error("start: no primary key");
|
||||
primary->start();
|
||||
update_last_received(); // set an upper bound on when we expect a response
|
||||
}
|
||||
@ -2503,7 +2533,8 @@ namespace openvpn {
|
||||
if (control_channel || process_events())
|
||||
{
|
||||
do {
|
||||
primary->flush();
|
||||
if (primary)
|
||||
primary->flush();
|
||||
if (secondary)
|
||||
secondary->flush();
|
||||
} while (process_events());
|
||||
@ -2517,7 +2548,8 @@ namespace openvpn {
|
||||
void housekeeping()
|
||||
{
|
||||
// handle control channel retransmissions on primary
|
||||
primary->retransmit();
|
||||
if (primary)
|
||||
primary->retransmit();
|
||||
|
||||
// handle control channel retransmissions on secondary
|
||||
if (secondary)
|
||||
@ -2537,7 +2569,9 @@ namespace openvpn {
|
||||
{
|
||||
if (!invalidated())
|
||||
{
|
||||
Time ret = primary->next_retransmit();
|
||||
Time ret = Time::infinite();
|
||||
if (primary)
|
||||
ret.min(primary->next_retransmit());
|
||||
if (secondary)
|
||||
ret.min(secondary->next_retransmit());
|
||||
ret.min(keepalive_xmit);
|
||||
@ -2588,6 +2622,8 @@ namespace openvpn {
|
||||
void data_encrypt(BufferAllocated& in_out)
|
||||
{
|
||||
//OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " DATA ENCRYPT size=" << in_out.size());
|
||||
if (!primary)
|
||||
throw proto_error("data_encrypt: no primary key");
|
||||
primary->encrypt(in_out);
|
||||
}
|
||||
|
||||
@ -2620,7 +2656,8 @@ namespace openvpn {
|
||||
// enter disconnected state
|
||||
void disconnect(const Error::Type reason)
|
||||
{
|
||||
primary->invalidate(reason);
|
||||
if (primary)
|
||||
primary->invalidate(reason);
|
||||
if (secondary)
|
||||
secondary->invalidate(reason);
|
||||
}
|
||||
@ -2629,7 +2666,7 @@ namespace openvpn {
|
||||
// they are disconnecting
|
||||
void send_explicit_exit_notify()
|
||||
{
|
||||
if (is_client() && is_udp())
|
||||
if (is_client() && is_udp() && primary)
|
||||
primary->send_explicit_exit_notify();
|
||||
}
|
||||
|
||||
@ -2640,7 +2677,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// can we call data_encrypt or data_decrypt yet?
|
||||
bool data_channel_ready() const { return primary->data_channel_ready(); }
|
||||
bool data_channel_ready() const { return primary && primary->data_channel_ready(); }
|
||||
|
||||
// total number of SSL/TLS negotiations during lifetime of ProtoContext object
|
||||
unsigned int negotiations() const { return n_key_ids; }
|
||||
@ -2649,7 +2686,7 @@ namespace openvpn {
|
||||
const Time::Duration& slowest_handshake() { return slowest_handshake_; }
|
||||
|
||||
// was primary context invalidated by an exception?
|
||||
bool invalidated() const { return primary->invalidated(); }
|
||||
bool invalidated() const { return primary && primary->invalidated(); }
|
||||
|
||||
// reason for invalidation if invalidated() above returns true
|
||||
Error::Type invalidation_reason() const { return primary->invalidation_reason(); }
|
||||
@ -2662,7 +2699,8 @@ namespace openvpn {
|
||||
dc_deferred = false;
|
||||
|
||||
// initialize data channel (crypto & compression)
|
||||
primary->init_data_channel();
|
||||
if (primary)
|
||||
primary->init_data_channel();
|
||||
if (secondary)
|
||||
secondary->init_data_channel();
|
||||
}
|
||||
@ -2707,7 +2745,7 @@ namespace openvpn {
|
||||
const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status)
|
||||
{
|
||||
if (key_id == primary->key_id())
|
||||
if (primary && key_id == primary->key_id())
|
||||
primary->data_limit_notify(cdl_mode, cdl_status);
|
||||
else if (secondary && key_id == secondary->key_id())
|
||||
secondary->data_limit_notify(cdl_mode, cdl_status);
|
||||
@ -2826,14 +2864,14 @@ namespace openvpn {
|
||||
const unsigned int flags = type.flags & (PacketType::DEFINED|PacketType::SECONDARY|PacketType::CONTROL);
|
||||
if (!control)
|
||||
{
|
||||
if (flags == (PacketType::DEFINED))
|
||||
if (flags == (PacketType::DEFINED) && primary)
|
||||
return *primary;
|
||||
else if (flags == (PacketType::DEFINED|PacketType::SECONDARY) && secondary)
|
||||
return *secondary;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags == (PacketType::DEFINED|PacketType::CONTROL))
|
||||
if (flags == (PacketType::DEFINED|PacketType::CONTROL) && primary)
|
||||
return *primary;
|
||||
else if (flags == (PacketType::DEFINED|PacketType::SECONDARY|PacketType::CONTROL) && secondary)
|
||||
return *secondary;
|
||||
@ -2850,6 +2888,8 @@ namespace openvpn {
|
||||
KeyContext& select_control_send_context()
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " CONTROL SEND");
|
||||
if (!primary)
|
||||
throw proto_error("select_control_send_context: no primary key");
|
||||
return *primary;
|
||||
}
|
||||
|
||||
@ -2860,7 +2900,7 @@ namespace openvpn {
|
||||
const Time now = *now_;
|
||||
|
||||
// check for keepalive timeouts
|
||||
if (now >= keepalive_xmit)
|
||||
if (now >= keepalive_xmit && primary)
|
||||
{
|
||||
primary->send_keepalive();
|
||||
update_last_sent();
|
||||
@ -2880,7 +2920,7 @@ namespace openvpn {
|
||||
bool did_work = false;
|
||||
|
||||
// primary
|
||||
if (primary->event_pending())
|
||||
if (primary && primary->event_pending())
|
||||
{
|
||||
process_primary_event();
|
||||
did_work = true;
|
||||
@ -2913,8 +2953,10 @@ namespace openvpn {
|
||||
void promote_secondary_to_primary()
|
||||
{
|
||||
primary.swap(secondary);
|
||||
primary->rekey(CryptoDCInstance::PROMOTE_SECONDARY_TO_PRIMARY);
|
||||
secondary->prepare_expire();
|
||||
if (primary)
|
||||
primary->rekey(CryptoDCInstance::PROMOTE_SECONDARY_TO_PRIMARY);
|
||||
if (secondary)
|
||||
secondary->prepare_expire();
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " PROMOTE_SECONDARY_TO_PRIMARY");
|
||||
}
|
||||
|
||||
@ -2965,7 +3007,8 @@ namespace openvpn {
|
||||
{
|
||||
case KeyContext::KEV_ACTIVE:
|
||||
secondary->rekey(CryptoDCInstance::NEW_SECONDARY);
|
||||
primary->prepare_expire();
|
||||
if (primary)
|
||||
primary->prepare_expire();
|
||||
break;
|
||||
case KeyContext::KEV_BECOME_PRIMARY:
|
||||
if (!secondary->invalidated())
|
||||
@ -2976,7 +3019,8 @@ namespace openvpn {
|
||||
secondary.reset();
|
||||
break;
|
||||
case KeyContext::KEV_RENEGOTIATE_QUEUE:
|
||||
primary->key_limit_reneg(KeyContext::KEV_RENEGOTIATE_FORCE, secondary->become_primary_time());
|
||||
if (primary)
|
||||
primary->key_limit_reneg(KeyContext::KEV_RENEGOTIATE_FORCE, secondary->become_primary_time());
|
||||
break;
|
||||
case KeyContext::KEV_NEGOTIATE:
|
||||
stats->error(Error::HANDSHAKE_TIMEOUT);
|
||||
|
@ -87,6 +87,7 @@ namespace openvpn {
|
||||
enum LoadFlags {
|
||||
LF_PARSE_MODE = (1<<0),
|
||||
LF_ALLOW_CLIENT_CERT_NOT_REQUIRED = (1<<1),
|
||||
LF_RELAY_MODE = (1<<2), // look for "relay-ca" instead of "ca" directive
|
||||
};
|
||||
|
||||
virtual void set_mode(const Mode& mode_arg) = 0;
|
||||
|
@ -72,9 +72,11 @@ namespace openvpn {
|
||||
throw option_error("tls-version-min: unrecognized TLS version");
|
||||
}
|
||||
|
||||
inline Type parse_tls_version_min(const OptionList& opt, const Type max_version)
|
||||
inline Type parse_tls_version_min(const OptionList& opt,
|
||||
const std::string& relay_prefix,
|
||||
const Type max_version)
|
||||
{
|
||||
const Option* o = opt.get_ptr("tls-version-min");
|
||||
const Option* o = opt.get_ptr(relay_prefix + "tls-version-min");
|
||||
if (o)
|
||||
{
|
||||
const std::string ver = o->get_optional(1, 16);
|
||||
|
@ -199,7 +199,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(asio::io_context& io_context,
|
||||
TransportClientParent& parent);
|
||||
TransportClientParent* parent);
|
||||
|
||||
private:
|
||||
ClientConfig()
|
||||
@ -225,7 +225,7 @@ namespace openvpn {
|
||||
{
|
||||
if (!config->http_proxy_options)
|
||||
{
|
||||
parent.proxy_error(Error::PROXY_ERROR, "http_proxy_options not defined");
|
||||
parent->proxy_error(Error::PROXY_ERROR, "http_proxy_options not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
// resolve it
|
||||
parent.transport_pre_resolve();
|
||||
parent->transport_pre_resolve();
|
||||
resolver.async_resolve(proxy_host, proxy_port,
|
||||
[self=Ptr(this)](const asio::error_code& error, asio::ip::tcp::resolver::results_type results)
|
||||
{
|
||||
@ -307,6 +307,16 @@ namespace openvpn {
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::TCPv4);
|
||||
else if (server_endpoint.address().is_v6())
|
||||
return Protocol(Protocol::TCPv6);
|
||||
else
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
@ -326,7 +336,7 @@ namespace openvpn {
|
||||
|
||||
Client(asio::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TransportClientParent& parent_arg)
|
||||
TransportClientParent* parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
socket(io_context_arg),
|
||||
config(config_arg),
|
||||
@ -341,6 +351,11 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
bool send_const(const Buffer& cbuf)
|
||||
{
|
||||
if (impl)
|
||||
@ -365,7 +380,7 @@ namespace openvpn {
|
||||
std::ostringstream os;
|
||||
os << "Transport error on '" << server_host << "' via HTTP proxy " << proxy_host << ':' << proxy_port << " : " << error;
|
||||
stop();
|
||||
parent.transport_error(Error::TRANSPORT_ERROR, os.str());
|
||||
parent->transport_error(Error::TRANSPORT_ERROR, os.str());
|
||||
}
|
||||
|
||||
void proxy_error(const Error::Type fatal_err, const std::string& what)
|
||||
@ -373,7 +388,7 @@ namespace openvpn {
|
||||
std::ostringstream os;
|
||||
os << "on " << proxy_host << ':' << proxy_port << ": " << what;
|
||||
stop();
|
||||
parent.proxy_error(fatal_err, os.str());
|
||||
parent->proxy_error(fatal_err, os.str());
|
||||
}
|
||||
|
||||
bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl
|
||||
@ -381,7 +396,7 @@ namespace openvpn {
|
||||
if (proxy_established)
|
||||
{
|
||||
if (!html_skip)
|
||||
parent.transport_recv(buf);
|
||||
parent->transport_recv(buf);
|
||||
else
|
||||
drain_html(buf); // skip extraneous HTML after header
|
||||
}
|
||||
@ -401,7 +416,7 @@ namespace openvpn {
|
||||
void tcp_write_queue_needs_send() // called by LinkImpl
|
||||
{
|
||||
if (proxy_established)
|
||||
parent.transport_needs_send();
|
||||
parent->transport_needs_send();
|
||||
}
|
||||
|
||||
void tcp_eof_handler() // called by LinkImpl
|
||||
@ -483,12 +498,12 @@ namespace openvpn {
|
||||
void proxy_connected(BufferAllocated& buf, const bool notify_parent)
|
||||
{
|
||||
proxy_established = true;
|
||||
if (parent.transport_is_openvpn_protocol())
|
||||
if (parent->transport_is_openvpn_protocol())
|
||||
{
|
||||
// switch socket from HTTP proxy handshake mode to OpenVPN protocol mode
|
||||
impl->set_raw_mode(false);
|
||||
if (notify_parent)
|
||||
parent.transport_connecting();
|
||||
parent->transport_connecting();
|
||||
try {
|
||||
impl->inject(buf);
|
||||
}
|
||||
@ -501,8 +516,8 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
if (notify_parent)
|
||||
parent.transport_connecting();
|
||||
parent.transport_recv(buf);
|
||||
parent->transport_connecting();
|
||||
parent->transport_recv(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,9 +530,9 @@ namespace openvpn {
|
||||
void proxy_half_connected()
|
||||
{
|
||||
proxy_established = true;
|
||||
if (parent.transport_is_openvpn_protocol())
|
||||
if (parent->transport_is_openvpn_protocol())
|
||||
impl->set_raw_mode_write(false);
|
||||
parent.transport_connecting();
|
||||
parent->transport_connecting();
|
||||
}
|
||||
|
||||
void drain_html(BufferAllocated& buf)
|
||||
@ -862,7 +877,7 @@ namespace openvpn {
|
||||
os << "DNS resolve error on '" << proxy_host << "' for TCP (HTTP proxy): " << error.message();
|
||||
config->stats->error(Error::RESOLVE_ERROR);
|
||||
stop();
|
||||
parent.transport_error(Error::UNDEF, os.str());
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -891,8 +906,8 @@ namespace openvpn {
|
||||
{
|
||||
proxy_remote_list().get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via HTTP Proxy");
|
||||
parent.transport_wait_proxy();
|
||||
parent.ip_hole_punch(server_endpoint_addr());
|
||||
parent->transport_wait_proxy();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#ifdef OPENVPN_PLATFORM_TYPE_UNIX
|
||||
if (config->socket_protect)
|
||||
@ -901,7 +916,7 @@ namespace openvpn {
|
||||
{
|
||||
config->stats->error(Error::SOCKET_PROTECT_ERROR);
|
||||
stop();
|
||||
parent.transport_error(Error::UNDEF, "socket_protect error (HTTP Proxy)");
|
||||
parent->transport_error(Error::UNDEF, "socket_protect error (HTTP Proxy)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -920,7 +935,7 @@ namespace openvpn {
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
parent.transport_wait();
|
||||
parent->transport_wait();
|
||||
impl.reset(new LinkImpl(this,
|
||||
socket,
|
||||
0, // send_queue_max_size is unlimited because we regulate size in cliproto.hpp
|
||||
@ -942,7 +957,7 @@ namespace openvpn {
|
||||
os << "TCP connect error on '" << proxy_host << ':' << proxy_port << "' (" << server_endpoint << ") for TCP-via-HTTP-proxy session: " << error.message();
|
||||
config->stats->error(Error::TCP_CONNECT_ERROR);
|
||||
stop();
|
||||
parent.transport_error(Error::UNDEF, os.str());
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -991,7 +1006,7 @@ namespace openvpn {
|
||||
asio::io_context& io_context;
|
||||
asio::ip::tcp::socket socket;
|
||||
ClientConfig::Ptr config;
|
||||
TransportClientParent& parent;
|
||||
TransportClientParent* parent;
|
||||
LinkImpl::Ptr impl;
|
||||
asio::ip::tcp::resolver resolver;
|
||||
LinkImpl::protocol::endpoint server_endpoint;
|
||||
@ -1011,7 +1026,7 @@ namespace openvpn {
|
||||
std::unique_ptr<HTTP::HTMLSkip> html_skip;
|
||||
};
|
||||
|
||||
inline TransportClient::Ptr ClientConfig::new_transport_client_obj(asio::io_context& io_context, TransportClientParent& parent)
|
||||
inline TransportClient::Ptr ClientConfig::new_transport_client_obj(asio::io_context& io_context, TransportClientParent* parent)
|
||||
{
|
||||
return TransportClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
166
openvpn/transport/client/relay.hpp
Normal file
166
openvpn/transport/client/relay.hpp
Normal file
@ -0,0 +1,166 @@
|
||||
// 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-2016 OpenVPN Technologies, 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(asio::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 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);
|
||||
}
|
||||
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) {}
|
||||
|
||||
// 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(asio::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;
|
||||
}
|
||||
|
||||
asio::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
|
@ -58,7 +58,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(asio::io_context& io_context,
|
||||
TransportClientParent& parent);
|
||||
TransportClientParent* parent);
|
||||
|
||||
private:
|
||||
ClientConfig()
|
||||
@ -88,7 +88,7 @@ namespace openvpn {
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.transport_pre_resolve();
|
||||
parent->transport_pre_resolve();
|
||||
resolver.async_resolve(server_host, server_port,
|
||||
[self=Ptr(this)](const asio::error_code& error, asio::ip::tcp::resolver::results_type results)
|
||||
{
|
||||
@ -150,13 +150,23 @@ namespace openvpn {
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::TCPv4);
|
||||
else if (server_endpoint.address().is_v6())
|
||||
return Protocol(Protocol::TCPv6);
|
||||
else
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
Client(asio::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TransportClientParent& parent_arg)
|
||||
TransportClientParent* parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
socket(io_context_arg),
|
||||
config(config_arg),
|
||||
@ -166,6 +176,11 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
bool send_const(const Buffer& cbuf)
|
||||
{
|
||||
if (impl)
|
||||
@ -193,13 +208,13 @@ namespace openvpn {
|
||||
|
||||
bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl
|
||||
{
|
||||
parent.transport_recv(buf);
|
||||
parent->transport_recv(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void tcp_write_queue_needs_send() // called by LinkImpl
|
||||
{
|
||||
parent.transport_needs_send();
|
||||
parent->transport_needs_send();
|
||||
}
|
||||
|
||||
void tcp_error_handler(const char *error) // called by LinkImpl
|
||||
@ -207,7 +222,7 @@ namespace openvpn {
|
||||
std::ostringstream os;
|
||||
os << "Transport error on '" << server_host << ": " << error;
|
||||
stop();
|
||||
parent.transport_error(Error::TRANSPORT_ERROR, os.str());
|
||||
parent->transport_error(Error::TRANSPORT_ERROR, os.str());
|
||||
}
|
||||
|
||||
void stop_()
|
||||
@ -241,7 +256,7 @@ namespace openvpn {
|
||||
os << "DNS resolve error on '" << server_host << "' for TCP session: " << error.message();
|
||||
config->stats->error(Error::RESOLVE_ERROR);
|
||||
stop();
|
||||
parent.transport_error(Error::UNDEF, os.str());
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,8 +266,8 @@ namespace openvpn {
|
||||
{
|
||||
config->remote_list->get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via TCP");
|
||||
parent.transport_wait();
|
||||
parent.ip_hole_punch(server_endpoint_addr());
|
||||
parent->transport_wait();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#ifdef OPENVPN_PLATFORM_TYPE_UNIX
|
||||
if (config->socket_protect)
|
||||
@ -261,7 +276,7 @@ namespace openvpn {
|
||||
{
|
||||
config->stats->error(Error::SOCKET_PROTECT_ERROR);
|
||||
stop();
|
||||
parent.transport_error(Error::UNDEF, "socket_protect error (TCP)");
|
||||
parent->transport_error(Error::UNDEF, "socket_protect error (TCP)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -290,9 +305,9 @@ namespace openvpn {
|
||||
impl->gremlin_config(config->gremlin_config);
|
||||
#endif
|
||||
impl->start();
|
||||
if (!parent.transport_is_openvpn_protocol())
|
||||
if (!parent->transport_is_openvpn_protocol())
|
||||
impl->set_raw_mode(true);
|
||||
parent.transport_connecting();
|
||||
parent->transport_connecting();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -300,7 +315,7 @@ namespace openvpn {
|
||||
os << "TCP 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());
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,7 +326,7 @@ namespace openvpn {
|
||||
asio::io_context& io_context;
|
||||
asio::ip::tcp::socket socket;
|
||||
ClientConfig::Ptr config;
|
||||
TransportClientParent& parent;
|
||||
TransportClientParent* parent;
|
||||
LinkImpl::Ptr impl;
|
||||
asio::ip::tcp::resolver resolver;
|
||||
LinkImpl::protocol::endpoint server_endpoint;
|
||||
@ -319,7 +334,7 @@ namespace openvpn {
|
||||
};
|
||||
|
||||
inline TransportClient::Ptr ClientConfig::new_transport_client_obj(asio::io_context& io_context,
|
||||
TransportClientParent& parent)
|
||||
TransportClientParent* parent)
|
||||
{
|
||||
return TransportClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
@ -35,8 +35,10 @@
|
||||
#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>
|
||||
@ -53,6 +55,8 @@ namespace openvpn {
|
||||
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
|
||||
@ -94,7 +98,8 @@ namespace openvpn {
|
||||
typedef RCPtr<TransportClientFactory> Ptr;
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(asio::io_context& io_context,
|
||||
TransportClientParent& parent) = 0;
|
||||
TransportClientParent* parent) = 0;
|
||||
virtual bool is_relay() { return false; }
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
@ -61,7 +61,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
virtual TransportClient::Ptr new_transport_client_obj(asio::io_context& io_context,
|
||||
TransportClientParent& parent);
|
||||
TransportClientParent* parent);
|
||||
|
||||
private:
|
||||
ClientConfig()
|
||||
@ -92,7 +92,7 @@ namespace openvpn {
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.transport_pre_resolve();
|
||||
parent->transport_pre_resolve();
|
||||
resolver.async_resolve(server_host, server_port,
|
||||
[self=Ptr(this)](const asio::error_code& error, asio::ip::udp::resolver::results_type results)
|
||||
{
|
||||
@ -148,13 +148,23 @@ namespace openvpn {
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::UDPv4);
|
||||
else if (server_endpoint.address().is_v6())
|
||||
return Protocol(Protocol::UDPv6);
|
||||
else
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
Client(asio::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TransportClientParent& parent_arg)
|
||||
TransportClientParent* parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
socket(io_context_arg),
|
||||
config(config_arg),
|
||||
@ -164,6 +174,11 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
bool send(const Buffer& buf)
|
||||
{
|
||||
if (impl)
|
||||
@ -177,7 +192,7 @@ namespace openvpn {
|
||||
if (err == EADDRNOTAVAIL)
|
||||
{
|
||||
stop();
|
||||
parent.transport_error(Error::TRANSPORT_ERROR, "EADDRNOTAVAIL: Can't assign requested address");
|
||||
parent->transport_error(Error::TRANSPORT_ERROR, "EADDRNOTAVAIL: Can't assign requested address");
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
@ -192,7 +207,7 @@ namespace openvpn {
|
||||
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);
|
||||
parent->transport_recv(pfp->buf);
|
||||
else
|
||||
config->stats->error(Error::BAD_SRC_ADDR);
|
||||
}
|
||||
@ -227,7 +242,7 @@ namespace openvpn {
|
||||
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());
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -237,8 +252,8 @@ namespace openvpn {
|
||||
{
|
||||
config->remote_list->get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via UDP");
|
||||
parent.transport_wait();
|
||||
parent.ip_hole_punch(server_endpoint_addr());
|
||||
parent->transport_wait();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#ifdef OPENVPN_PLATFORM_TYPE_UNIX
|
||||
if (config->socket_protect)
|
||||
@ -247,7 +262,7 @@ namespace openvpn {
|
||||
{
|
||||
config->stats->error(Error::SOCKET_PROTECT_ERROR);
|
||||
stop();
|
||||
parent.transport_error(Error::UNDEF, "socket_protect error (UDP)");
|
||||
parent->transport_error(Error::UNDEF, "socket_protect error (UDP)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -273,7 +288,7 @@ namespace openvpn {
|
||||
impl->gremlin_config(config->gremlin_config);
|
||||
#endif
|
||||
impl->start(config->n_parallel);
|
||||
parent.transport_connecting();
|
||||
parent->transport_connecting();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -281,7 +296,7 @@ namespace openvpn {
|
||||
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());
|
||||
parent->transport_error(Error::UNDEF, os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -292,7 +307,7 @@ namespace openvpn {
|
||||
asio::io_context& io_context;
|
||||
asio::ip::udp::socket socket;
|
||||
ClientConfig::Ptr config;
|
||||
TransportClientParent& parent;
|
||||
TransportClientParent* parent;
|
||||
LinkImpl::Ptr impl;
|
||||
asio::ip::udp::resolver resolver;
|
||||
UDPTransport::AsioEndpoint server_endpoint;
|
||||
@ -300,7 +315,7 @@ namespace openvpn {
|
||||
};
|
||||
|
||||
inline TransportClient::Ptr ClientConfig::new_transport_client_obj(asio::io_context& io_context,
|
||||
TransportClientParent& parent)
|
||||
TransportClientParent* parent)
|
||||
{
|
||||
return TransportClient::Ptr(new Client(io_context, this, parent));
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ namespace openvpn {
|
||||
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.
|
||||
|
@ -143,7 +143,7 @@ namespace openvpn {
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << "units : " << errinfo(eno));
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -72,6 +72,9 @@ namespace openvpn {
|
||||
// set fwmark
|
||||
virtual void set_fwmark(const unsigned int fwmark) = 0;
|
||||
|
||||
// set up relay to target
|
||||
virtual void relay(const IP::Addr& target, const int port) = 0;
|
||||
|
||||
virtual const std::string& tun_info() const = 0;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user