0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 19:52:15 +02:00

Added AltProxy API that allows for alternative proxy implementations

to be developed outside the core.
This commit is contained in:
James Yonan 2015-02-02 23:11:51 -07:00
parent d5f249b3a4
commit 557df02296
14 changed files with 300 additions and 42 deletions

View File

@ -301,7 +301,8 @@ namespace openvpn {
{
ClientState() : conn_timeout(0), tun_persist(false),
google_dns_fallback(false), disable_client_cert(false),
default_key_direction(-1), force_aes_cbc_ciphersuites(false) {}
default_key_direction(-1), force_aes_cbc_ciphersuites(false),
alt_proxy(false) {}
OptionList options;
EvalConfig eval;
@ -326,6 +327,7 @@ namespace openvpn {
std::string gui_version;
ProtoContextOptions::Ptr proto_context_options;
HTTPProxyTransport::Options::Ptr http_proxy_options;
bool alt_proxy;
};
};
@ -416,6 +418,7 @@ namespace openvpn {
state->default_key_direction = config.defaultKeyDirection;
state->force_aes_cbc_ciphersuites = config.forceAesCbcCiphersuites;
state->gui_version = config.guiVersion;
state->alt_proxy = config.altProxy;
if (!config.proxyHost.empty())
{
HTTPProxyTransport::Options::Ptr ho(new HTTPProxyTransport::Options());
@ -597,6 +600,7 @@ namespace openvpn {
cc.google_dns_fallback = state->google_dns_fallback;
cc.proto_context_options = state->proto_context_options;
cc.http_proxy_options = state->http_proxy_options;
cc.alt_proxy = state->alt_proxy;
cc.reconnect_notify = &state->reconnect_notify;
cc.private_key_password = state->private_key_password;
cc.disable_client_cert = state->disable_client_cert;

View File

@ -151,7 +151,8 @@ namespace openvpn {
Config() : connTimeout(0), tunPersist(false), googleDnsFallback(false),
disableClientCert(false), defaultKeyDirection(-1),
forceAesCbcCiphersuites(false),
proxyAllowCleartextAuth(false) {}
proxyAllowCleartextAuth(false),
altProxy(false) {}
// OpenVPN profile as a string
std::string content;
@ -219,6 +220,9 @@ namespace openvpn {
std::string proxyUsername; // proxy credentials (optional)
std::string proxyPassword; // proxy credentials (optional)
bool proxyAllowCleartextAuth; // enables HTTP Basic auth
// Custom proxy implementation
bool altProxy;
};
// used to communicate VPN events such as connect, disconnect, etc.

View File

@ -22,6 +22,12 @@ else
fi
echo PACKAGE $PKG
if [ -d "$O3/common" ]; then
common="-I$O3/common -DPRIVATE_TUNNEL_PROXY"
else
common=""
fi
echo SWIG
swig -c++ -java -package $pkg -I$O3/core/client -I$O3/core ovpncli.i
@ -64,6 +70,7 @@ g++ \
-DHAVE_LZ4 \
-I$O3/core/client \
-I$O3/core \
$common \
-I$DEP_DIR/boost \
$ssl_inc \
-I$DEP_DIR/snappy/snappy-$PLATFORM/include \
@ -80,6 +87,7 @@ g++ \
$vis1 $vis2 \
-I$O3/core/client \
-I$O3/core \
$common \
-L$DEP_DIR/boost/stage-$PLATFORM/lib \
$ssl_libdir \
-L$DEP_DIR/minicrypto/minicrypto-$PLATFORM \

View File

@ -98,8 +98,9 @@ namespace openvpn {
if (!test_network())
throw ErrorCode(Error::NETWORK_UNAVAILABLE, true, "Network Unavailable");
RemoteList::Ptr remote_list = client_options->remote_list_precache();
RemoteList::PreResolve::Ptr preres(new RemoteList::PreResolve(io_service,
client_options->remote_list_ptr(),
remote_list,
client_options->stats_ptr()));
if (preres->work_available())
{

View File

@ -43,7 +43,7 @@
#include <openvpn/transport/client/udpcli.hpp>
#include <openvpn/transport/client/tcpcli.hpp>
#include <openvpn/transport/client/httpcli.hpp>
#include <openvpn/transport/altproxy.hpp>
#include <openvpn/client/cliproto.hpp>
#include <openvpn/client/cliopthelper.hpp>
#include <openvpn/client/optfilt.hpp>
@ -68,6 +68,10 @@
#include <openvpn/tun/client/tunnull.hpp>
#endif
#ifdef PRIVATE_TUNNEL_PROXY
#include <openvpn/pt/ptproxy.hpp>
#endif
namespace openvpn {
class ClientOptions : public RC<thread_unsafe_refcount>
@ -84,6 +88,7 @@ namespace openvpn {
socket_protect = NULL;
reconnect_notify = NULL;
conn_timeout = 0;
alt_proxy = false;
tun_persist = false;
google_dns_fallback = false;
disable_client_cert = false;
@ -102,6 +107,7 @@ namespace openvpn {
ClientEvent::Queue::Ptr cli_events;
ProtoContextOptions::Ptr proto_context_options;
HTTPProxyTransport::Options::Ptr http_proxy_options;
bool alt_proxy;
bool tun_persist;
bool google_dns_fallback;
std::string private_key_password;
@ -141,10 +147,12 @@ namespace openvpn {
userlocked_username = pcc.userlockedUsername();
autologin = pcc.autologin();
// digest factory
DigestFactory::Ptr digest_factory(new CryptoDigestFactory<SSLLib::CryptoAPI>());
// initialize RNG/PRNG
rng.reset(new SSLLib::RandomAPI(false));
#if ENABLE_PRNG
DigestFactory::Ptr digest_factory(new CryptoDigestFactory<SSLLib::CryptoAPI>());
prng.reset(new PRNG("SHA1", digest_factory, rng, 16)); // fixme: hangs on OS X 10.6 with USE_POLARSSL_APPLE_HYBRID
#else
prng.reset(new SSLLib::RandomAPI(true));
@ -188,12 +196,17 @@ namespace openvpn {
cp->rng = rng;
cp->prng = prng;
#ifdef PRIVATE_TUNNEL_PROXY
if (config.alt_proxy)
alt_proxy = PTProxy::new_proxy(opt);
#endif
// If HTTP proxy parameters are not supplied by API, try to get them from config
if (!http_proxy_options)
http_proxy_options = HTTPProxyTransport::Options::parse(opt);
// load remote list
remote_list.reset(new RemoteList(opt, true));
remote_list.reset(new RemoteList(opt, "", true));
if (!remote_list->defined())
throw option_error("no remote option specified");
@ -206,7 +219,8 @@ namespace openvpn {
remote_list->set_server_override(config.server_override);
// process protocol override, should be called after set_enable_cache
remote_list->handle_proto_override(config.proto_override, bool(http_proxy_options));
remote_list->handle_proto_override(config.proto_override,
http_proxy_options || (alt_proxy && alt_proxy->requires_tcp()));
// process remote-random
if (opt.exists("remote-random"))
@ -215,8 +229,13 @@ namespace openvpn {
// get "float" option
server_addr_float = opt.exists("float");
// special remote cache handling for HTTP proxy
if (http_proxy_options)
// special remote cache handling for proxies
if (alt_proxy)
{
remote_list->set_enable_cache(false); // remote server addresses will be resolved by proxy
alt_proxy->set_enable_cache(config.tun_persist);
}
else if (http_proxy_options)
{
remote_list->set_enable_cache(false); // remote server addresses will be resolved by proxy
http_proxy_options->proxy_server_set_enable_cache(config.tun_persist);
@ -349,7 +368,12 @@ namespace openvpn {
void next()
{
remote_list->next();
bool omit_next = false;
if (alt_proxy)
omit_next = alt_proxy->next();
if (!omit_next)
remote_list->next();
load_transport_config();
}
@ -410,11 +434,28 @@ namespace openvpn {
SessionStats& stats() { return *cli_stats; }
const SessionStats::Ptr& stats_ptr() const { return cli_stats; }
ClientEvent::Queue& events() { return *cli_events; }
const RemoteList::Ptr& remote_list_ptr() const { return remote_list; }
ClientLifeCycle* lifecycle() { return client_lifecycle.get(); }
int conn_timeout() const { return conn_timeout_; }
RemoteList::Ptr remote_list_precache() const
{
RemoteList::Ptr r;
if (alt_proxy)
{
alt_proxy->precache(r);
if (r)
return r;
}
if (http_proxy_options)
{
http_proxy_options->proxy_server_precache(r);
if (r)
return r;
}
return remote_list;
}
void update_now()
{
now_.update();
@ -435,11 +476,26 @@ namespace openvpn {
// 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.
// construct transport object
if (http_proxy_options)
if (alt_proxy)
{
if (alt_proxy->requires_tcp() && !transport_protocol.is_tcp())
throw option_error("internal error: no TCP server entries for " + alt_proxy->name() + " transport");
AltProxy::Config conf;
conf.remote_list = remote_list;
conf.frame = frame;
conf.stats = cli_stats;
conf.digest_factory.reset(new CryptoDigestFactory<SSLLib::CryptoAPI>());
conf.socket_protect = socket_protect;
conf.rng = rng;
transport_factory = alt_proxy->new_transport_client_factory(conf);
}
else if (http_proxy_options)
{
// HTTP proxy always uses TCP. If current transport protocol is not TCP, this is
// an error that should have been caught earlier in RemoteList::handle_proto_override.
if (!transport_protocol.is_tcp())
throw option_error("internal error: no TCP server entries for HTTP proxy transport");
@ -510,6 +566,7 @@ namespace openvpn {
PushOptionsBase::Ptr push_base;
OptionList::FilterBase::Ptr pushed_options_filter;
ClientLifeCycle::Ptr client_lifecycle;
AltProxy::Ptr alt_proxy;
};
}

View File

@ -160,7 +160,7 @@ namespace openvpn {
}
// validate remote list
RemoteList rl(options, false);
RemoteList rl(options, "", false);
// determine if private key is encrypted
if (!externalPki_)

View File

@ -55,6 +55,23 @@ namespace openvpn {
class RemoteList : public RC<thread_unsafe_refcount>
{
// Directive names that we search for in options
struct Directives
{
void init(const std::string& prefix)
{
connection = prefix + "connection";
remote = prefix + "remote";
proto = prefix + "proto";
port = prefix + "port";
}
std::string connection;
std::string remote;
std::string proto;
std::string port;
};
// A single IP address that is part of a list of IP addresses
// associated with a "remote" item.
struct ResolvedAddr : public RC<thread_unsafe_refcount>
@ -339,20 +356,26 @@ namespace openvpn {
size_t index;
};
static bool directives_defined(const OptionList& opt, const std::string& directive_prefix)
{
Directives d;
d.init(directive_prefix);
return opt.get_index_ptr(d.connection) || opt.get_index_ptr(d.remote);
}
// create an empty remote list
RemoteList()
{
init();
init("");
}
// create a remote list with exactly one item
RemoteList(const std::string& server_host,
const std::string& server_port,
const Protocol& transport_protocol,
const std::string& title)
{
init();
init("");
validate_port(server_port, title);
@ -364,9 +387,9 @@ namespace openvpn {
}
// create a remote list from config file option list
RemoteList(const OptionList& opt, bool warn)
RemoteList(const OptionList& opt, const std::string& directive_prefix, const bool warn)
{
init();
init(directive_prefix);
// handle remote, port, and proto at the top-level
Protocol default_proto(Protocol::UDPv4);
@ -376,7 +399,7 @@ namespace openvpn {
// cycle through <connection> blocks
{
const size_t max_conn_block_size = 4096;
const OptionList::IndexList* conn = opt.get_index_ptr("connection");
const OptionList::IndexList* conn = opt.get_index_ptr(directives.connection);
if (conn)
{
for (OptionList::IndexList::const_iterator i = conn->begin(); i != conn->end(); ++i)
@ -427,6 +450,11 @@ namespace openvpn {
enable_cache = enable_cache_arg;
}
bool get_enable_cache() const
{
return enable_cache;
}
// override all server hosts to server_override
void set_server_override(const std::string& server_override)
{
@ -466,17 +494,17 @@ namespace openvpn {
}
// Higher-level version of set_proto_override that also supports indication
// on whether or not HTTP proxy is enabled. Should be called after set_enable_cache
// on whether or not TCP-based proxies are enabled. Should be called after set_enable_cache
// because it may modify enable_cache flag.
void handle_proto_override(const Protocol& proto_override, const bool http_proxy_enabled)
void handle_proto_override(const Protocol& proto_override, const bool tcp_proxy_enabled)
{
if (http_proxy_enabled)
if (tcp_proxy_enabled)
{
const Protocol tcp(Protocol::TCP);
if (contains_protocol(tcp))
set_proto_override(tcp);
else
throw option_error("cannot connect via HTTP proxy because no TCP server entries exist in profile");
throw option_error("cannot connect via TCP-based proxy because no TCP server entries exist in profile");
}
else if (proto_override.defined() && contains_protocol(proto_override))
set_proto_override(proto_override);
@ -589,9 +617,10 @@ namespace openvpn {
private:
// initialization, called by constructors
void init()
void init(const std::string& directive_prefix)
{
enable_cache = false;
directives.init(directive_prefix);
}
// reset the cache associated with all items
@ -702,24 +731,24 @@ namespace openvpn {
{
// parse "proto" option if present
{
const Option* o = opt.get_ptr("proto");
const Option* o = opt.get_ptr(directives.proto);
if (o)
default_proto = Protocol::parse(o->get(1, 16), true);
}
// parse "port" option if present
{
const Option* o = opt.get_ptr("port");
const Option* o = opt.get_ptr(directives.port);
if (o)
{
default_port = o->get(1, 16);
validate_port(default_port, "port");
validate_port(default_port, directives.port);
}
}
// cycle through remote entries
{
const OptionList::IndexList* rem = opt.get_index_ptr("remote");
const OptionList::IndexList* rem = opt.get_index_ptr(directives.remote);
if (rem)
{
for (OptionList::IndexList::const_iterator i = rem->begin(); i != rem->end(); ++i)
@ -731,7 +760,7 @@ namespace openvpn {
if (o.size() >= 3)
{
e->server_port = o.get(2, 16);
validate_port(e->server_port, "port");
validate_port(e->server_port, directives.port);
}
else
e->server_port = default_port;
@ -755,6 +784,8 @@ namespace openvpn {
Index index;
std::vector<Item::Ptr> list;
Directives directives;
};
}

View File

@ -0,0 +1,76 @@
// 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-2015 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/>.
#ifndef OPENVPN_TRANSPORT_ALTPROXY_H
#define OPENVPN_TRANSPORT_ALTPROXY_H
#include <string>
#include <openvpn/common/rc.hpp>
#include <openvpn/transport/client/transbase.hpp>
namespace openvpn {
struct AltProxy : public RC<thread_unsafe_refcount>
{
struct Config
{
Config()
: send_queue_max_size(1024),
free_list_max_size(8),
socket_protect(NULL)
{}
RemoteList::Ptr remote_list;
size_t send_queue_max_size;
size_t free_list_max_size;
Frame::Ptr frame;
SessionStats::Ptr stats;
RandomAPI::Ptr rng;
DigestFactory::Ptr digest_factory;
SocketProtect* socket_protect;
};
typedef boost::intrusive_ptr<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

View File

@ -95,6 +95,12 @@ namespace openvpn {
proxy_server->set_enable_cache(enable_cache);
}
void proxy_server_precache(RemoteList::Ptr& r)
{
if (proxy_server->get_enable_cache())
r = proxy_server;
}
static Ptr parse(const OptionList& opt)
{
if (opt.exists("http-proxy"))

View File

@ -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-2015 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/>.
#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 boost::intrusive_ptr<TransportMutateStream> Ptr;
virtual void pre_send(BufferAllocated& buf) = 0;
virtual void post_recv(BufferAllocated& buf) = 0;
};
}
#endif

View File

@ -25,6 +25,7 @@
#define OPENVPN_TRANSPORT_TCPLINK_H
#include <deque>
#include <utility> // for std::move
#include <boost/asio.hpp>
@ -36,6 +37,7 @@
#include <openvpn/frame/frame.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/transport/pktstream.hpp>
#include <openvpn/transport/mutate.hpp>
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1
#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x)
@ -105,6 +107,11 @@ namespace openvpn {
return raw_mode;
}
void set_mutate(const TransportMutateStream::Ptr& mutate_arg)
{
mutate = mutate_arg;
}
bool send(BufferAllocated& b)
{
if (halt)
@ -128,8 +135,12 @@ namespace openvpn {
buf.reset(new BufferAllocated());
buf->swap(b);
if (!is_raw_mode())
PacketStream::prepend_size(*buf);
queue.push_back(buf);
{
PacketStream::prepend_size(*buf);
if (mutate)
mutate->pre_send(*buf);
}
queue.push_back(std::move(buf));
if (queue.size() == 1) // send operation not currently active?
queue_send();
return true;
@ -192,7 +203,7 @@ namespace openvpn {
if (free_list.size() < free_list_max_size)
{
buf->reset_content();
free_list.push_back(buf); // recycle the buffer for later use
free_list.push_back(std::move(buf)); // recycle the buffer for later use
}
}
else if (bytes_sent < buf->size())
@ -242,10 +253,20 @@ namespace openvpn {
pfp->buf.set_size(bytes_recvd);
if (!is_raw_mode())
{
BufferAllocated pkt;
put_pktstream(pfp->buf, pkt);
if (!pfp->buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer
pfp->buf.move(pkt);
try {
BufferAllocated pkt;
put_pktstream(pfp->buf, pkt);
if (!pfp->buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer
pfp->buf.move(pkt);
}
catch (const std::exception& e)
{
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract error: " << e.what());
stats->error(Error::TCP_SIZE_ERROR);
read_handler->tcp_error_handler("TCP_SIZE_ERROR");
stop();
return;
}
}
else
read_handler->tcp_read_handler(pfp->buf);
@ -271,6 +292,8 @@ namespace openvpn {
{
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);
@ -293,6 +316,7 @@ namespace openvpn {
Queue queue; // send queue
Queue free_list; // recycled free buffers for send queue
PacketStream pktstream;
TransportMutateStream::Ptr mutate;
};
}
} // namespace openvpn

View File

@ -1 +1,2 @@
cli
cli.dSYM

View File

@ -189,6 +189,7 @@ int main(int argc, char *argv[])
{ "proxy-username", required_argument, NULL, 'U' },
{ "proxy-password", required_argument, NULL, 'W' },
{ "proxy-basic", no_argument, NULL, 'B' },
{ "alt-proxy", no_argument, NULL, 'A' },
{ "eval", no_argument, NULL, 'e' },
{ "self-test", no_argument, NULL, 'T' },
{ "cache-password", no_argument, NULL, 'C' },
@ -232,10 +233,11 @@ int main(int argc, char *argv[])
bool tunPersist = false;
bool merge = false;
bool version = false;
bool altProxy = false;
int ch;
while ((ch = getopt_long(argc, argv, "BeTCxfgjmvu:p:r:P:s:t:c:z:h:q:U:W:k:", longopts, NULL)) != -1)
while ((ch = getopt_long(argc, argv, "BAeTCxfgjmvu:p:r:P:s:t:c:z:h:q:U:W:k:", longopts, NULL)) != -1)
{
switch (ch)
{
@ -290,6 +292,9 @@ int main(int argc, char *argv[])
case 'B':
proxyAllowCleartextAuth = true;
break;
case 'A':
altProxy = true;
break;
case 'f':
forceAesCbcCiphersuites = true;
break;
@ -368,6 +373,7 @@ int main(int argc, char *argv[])
config.proxyUsername = proxyUsername;
config.proxyPassword = proxyPassword;
config.proxyAllowCleartextAuth = proxyAllowCleartextAuth;
config.altProxy = altProxy;
config.defaultKeyDirection = defaultKeyDirection;
config.forceAesCbcCiphersuites = forceAesCbcCiphersuites;
config.googleDnsFallback = googleDnsFallback;

View File

@ -1,15 +1,15 @@
#!/bin/bash
[ "$NULL" = "1" ] && export GCC_EXTRA="-DOPENVPN_FORCE_TUN_NULL -DTUN_NULL_EXIT"
[ "$NULL" = "1" ] && export GCC_EXTRA="$GCC_EXTRA -DOPENVPN_FORCE_TUN_NULL -DTUN_NULL_EXIT"
if [ "$(uname)" == "Darwin" ]; then
cd $O3/core
. vars/vars-osx64
. vars/setpath
cd test/ovpncli
PSSL=1 SNAP=1 LZ4=1 build cli 2>&1
PSSL=1 SNAP=1 LZ4=1 PTPROXY=1 build cli 2>&1
else
cd $O3/core
. vars/vars-linux
. vars/setpath
cd test/ovpncli
PSSL=1 SNAP=1 LZ4=1 NOSSL=1 build cli 2>&1
PSSL=1 SNAP=1 LZ4=1 NOSSL=1 PTPROXY=1 build cli 2>&1
fi