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

Added "remote" option list handling.

Added ClientProtoTerminateCallback abstraction to ClientProto.

Added ClientProto::Config for configuration parameters, rather than
passing parameters individually to ClientProto constructor.
This commit is contained in:
James Yonan 2012-02-06 08:28:05 +00:00
parent e7a5d9f55b
commit f9ae757e1a
5 changed files with 214 additions and 68 deletions

View File

@ -2,7 +2,8 @@
#define OPENVPN_CLIENT_CLIPROTO_H
#include <boost/asio.hpp>
#include <boost/cstdint.hpp>
#include <boost/cstdint.hpp> // for boost::uint...
#include <boost/algorithm/string.hpp> // for boost::algorithm::starts_with
#include <openvpn/tun/client/tunbase.hpp>
#include <openvpn/transport/client/transbase.hpp>
@ -21,7 +22,10 @@
namespace openvpn {
// OpenVPN client
struct ClientProtoTerminateCallback {
virtual void client_proto_terminate() = 0;
};
template <typename SSL_CONTEXT>
class ClientProto : public ProtoContext<SSL_CONTEXT>, TransportClientParent, TunClientParent
{
@ -32,6 +36,7 @@ namespace openvpn {
public:
typedef boost::intrusive_ptr<ClientProto> Ptr;
typedef typename Base::Config ProtoConfig;
OPENVPN_EXCEPTION(client_exception);
OPENVPN_EXCEPTION(tun_exception);
@ -39,28 +44,32 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(session_invalidated);
OPENVPN_SIMPLE_EXCEPTION(auth_failed);
struct Config {
typename Base::Config::Ptr proto_context_config;
TransportClientFactory::Ptr transport_factory;
TunClientFactory::Ptr tun_factory;
SessionStats::Ptr cli_stats;
ClientEvent::Queue::Ptr cli_events;
std::string username;
std::string password;
};
ClientProto(boost::asio::io_service& io_service_arg,
const typename Base::Config::Ptr& config,
const TransportClientFactory::Ptr& transport_factory_arg,
const TunClientFactory::Ptr& tun_factory_arg,
const SessionStats::Ptr& stats_arg,
const ClientEvent::Queue::Ptr& cli_events_arg,
const bool client_throw_arg,
const std::string& username_arg,
const std::string& password_arg)
: Base(config, stats_arg),
const Config& config,
ClientProtoTerminateCallback* terminate_callback_arg)
: Base(config.proto_context_config, config.cli_stats),
io_service(io_service_arg),
transport_factory(transport_factory_arg),
tun_factory(tun_factory_arg),
client_throw(client_throw_arg),
transport_factory(config.transport_factory),
tun_factory(config.tun_factory),
terminate_callback(terminate_callback_arg),
housekeeping_timer(io_service_arg),
push_request_timer(io_service_arg),
stopped(false),
username(username_arg),
password(password_arg),
first_packet_received(false),
username(config.username),
password(config.password),
first_packet_received_(false),
sent_push_request(false),
cli_events(cli_events_arg)
cli_events(config.cli_events)
{
#ifdef OPENVPN_PACKET_LOG
packet_log.open(OPENVPN_PACKET_LOG, std::ios::binary);
@ -83,6 +92,8 @@ namespace openvpn {
}
#endif
bool first_packet_received() const { return first_packet_received_; }
void start()
{
// coarse wakeup range
@ -95,26 +106,28 @@ namespace openvpn {
void stop_on_signal(const boost::system::error_code& error, int signal_number)
{
stop();
stop(true);
}
void stop()
void stop(const bool call_terminate_callback)
{
if (!stopped)
{
stopped = true;
housekeeping_timer.cancel();
push_request_timer.cancel();
if (tun)
tun->stop();
if (transport)
transport->stop();
stopped = true;
if (terminate_callback && call_terminate_callback)
terminate_callback->client_proto_terminate();
}
}
virtual ~ClientProto()
{
stop();
stop(false);
}
private:
@ -128,11 +141,11 @@ namespace openvpn {
Base::update_now();
// log connecting event (only on first packet received)
if (!first_packet_received)
if (!first_packet_received_)
{
ClientEvent::Base::Ptr ev = new ClientEvent::Connecting();
cli_events->add_event(ev);
first_packet_received = true;
first_packet_received_ = true;
}
// get packet type
@ -241,13 +254,13 @@ namespace openvpn {
virtual void transport_error(const std::exception& err)
{
if (client_throw)
throw transport_exception(err.what());
else
if (terminate_callback)
{
OPENVPN_LOG("Transport Error: " << err.what());
stop();
stop(true);
}
else
throw transport_exception(err.what());
}
void extract_auth_token(const OptionList& opt)
@ -300,13 +313,13 @@ namespace openvpn {
else if (boost::starts_with(msg, "AUTH_FAILED"))
{
Base::stat().error(Error::AUTH_FAIL);
if (client_throw)
throw auth_failed();
else
if (terminate_callback)
{
OPENVPN_LOG("AUTH_FAILED");
stop();
stop(true);
}
else
throw auth_failed();
}
}
@ -336,13 +349,13 @@ namespace openvpn {
virtual void tun_error(const std::exception& err)
{
if (client_throw)
throw tun_exception(err.what());
else
if (terminate_callback)
{
OPENVPN_LOG("TUN Error: " << err.what());
stop();
stop(true);
}
else
throw tun_exception(err.what());
}
// proto base class calls here to get auth credentials
@ -405,13 +418,13 @@ namespace openvpn {
Base::housekeeping();
if (Base::invalidated())
{
if (client_throw)
throw session_invalidated();
else
if (terminate_callback)
{
OPENVPN_LOG("Session invalidated");
stop();
stop(true);
}
else
throw session_invalidated();
}
set_housekeeping_timer();
}
@ -443,13 +456,13 @@ namespace openvpn {
void process_exception(std::exception& e)
{
if (client_throw)
throw client_exception(e.what());
else
if (terminate_callback)
{
OPENVPN_LOG("Client exception: " << e.what());
stop();
stop(true);
}
else
throw client_exception(e.what());
}
boost::asio::io_service& io_service;
@ -460,7 +473,7 @@ namespace openvpn {
TunClientFactory::Ptr tun_factory;
TunClient::Ptr tun;
bool client_throw;
ClientProtoTerminateCallback* terminate_callback;
CoarseTime housekeeping_schedule;
AsioTimer housekeeping_timer;
@ -472,7 +485,7 @@ namespace openvpn {
std::string username;
std::string password;
bool first_packet_received;
bool first_packet_received_;
bool sent_push_request;
ClientEvent::Queue::Ptr cli_events;

View File

@ -187,7 +187,7 @@ namespace openvpn {
const Option& get_first(const std::string& name) const
{
OptionList::IndexMap::const_iterator e = map_.find(name);
IndexMap::const_iterator e = map_.find(name);
if (e != map_.end() && !e->second.empty())
return (*this)[e->second[0]];
else
@ -196,7 +196,7 @@ namespace openvpn {
const Option* get_ptr(const std::string& name) const
{
OptionList::IndexMap::const_iterator e = map_.find(name);
IndexMap::const_iterator e = map_.find(name);
if (e != map_.end() && !e->second.empty())
{
if (e->second.size() == 1)
@ -217,6 +217,24 @@ namespace openvpn {
OPENVPN_THROW(option_error, "option '" << name << "' not found");
}
const IndexList& get_index(const std::string& name) const
{
IndexMap::const_iterator e = map_.find(name);
if (e != map_.end() && !e->second.empty())
return e->second;
else
OPENVPN_THROW(option_error, "option '" << name << "' not found");
}
const IndexList* get_index_ptr(const std::string& name) const
{
IndexMap::const_iterator e = map_.find(name);
if (e != map_.end() && !e->second.empty())
return &e->second;
else
return NULL;
}
bool exists(const std::string& name) const
{
const Option* o = get_ptr(name);

View File

@ -0,0 +1,70 @@
#ifndef OPENVPN_OPTIONS_REMOTELIST_H
#define OPENVPN_OPTIONS_REMOTELIST_H
#include <string>
#include <sstream>
#include <vector>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/transport/protocol.hpp>
namespace openvpn {
class RemoteList : public RC<thread_unsafe_refcount>
{
public:
typedef boost::intrusive_ptr<RemoteList> Ptr;
struct Item
{
std::string server_host;
std::string server_port;
Protocol transport_protocol;
};
RemoteList(const OptionList& opt)
{
const OptionList::IndexList& rem = opt.get_index("remote");
for (OptionList::IndexList::const_iterator i = rem.begin(); i != rem.end(); i++)
{
Item e;
const Option& o = opt[*i];
e.server_host = o.get(1);
e.server_port = o.get(2);
e.transport_protocol = Protocol::parse(o.get(3));
list.push_back(e);
}
}
// used to cycle through Item list
const Item& modulo_ref(const size_t i) { return list[i % list.size()]; }
size_t size() const { return list.size(); }
const Item& operator[](const size_t i) { return list[i]; }
#ifdef OPENVPN_DEBUG
std::string debug_render() const
{
std::ostringstream out;
for (size_t i = 0; i < list.size(); ++i)
{
const Item& e = list[i];
out << '[' << i
<< "] host=" << e.server_host
<< " port=" << e.server_port
<< " proto=" << e.transport_protocol.str()
<< std::endl;
}
return out.str();
}
#endif
private:
std::vector<Item> list;
};
}
#endif

View File

@ -29,6 +29,7 @@
#include <openvpn/transport/protocol.hpp>
#include <openvpn/tun/layer.hpp>
#include <openvpn/compress/compress.hpp>
#include <openvpn/options/remotelist.hpp>
#ifdef OPENVPN_DEBUG_PROTO
#define OPENVPN_LOG_PROTO(x) OPENVPN_LOG(x)
@ -163,6 +164,7 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(peer_psid_undef);
OPENVPN_SIMPLE_EXCEPTION(bad_auth_prefix);
OPENVPN_EXCEPTION(process_server_push_error);
OPENVPN_EXCEPTION_INHERIT(option_error, proto_option_error);
static unsigned int mtu()
{
@ -249,24 +251,8 @@ namespace openvpn {
keepalive_ping = Time::Duration::seconds(8);
keepalive_timeout = Time::Duration::seconds(40);
comp_ctx = CompressContext(CompressContext::NONE);
// tcp/udp
{
const Option& o = opt.get_first("remote");
const std::string& proto = o.get(3);
if (proto == "udp")
{
protocol = Protocol(Protocol::UDPv4);
pid_mode = PacketIDReceive::UDP_MODE;
}
else if (proto == "tcp")
{
protocol = Protocol(Protocol::TCPv4);
pid_mode = PacketIDReceive::TCP_MODE;
}
else
throw option_error("bad protocol");
}
protocol = Protocol();
pid_mode = PacketIDReceive::UDP_MODE;
// layer
{
@ -276,7 +262,7 @@ namespace openvpn {
else if (dev_type == "tap")
layer = Layer(Layer::OSI_LAYER_2);
else
throw option_error("bad dev-type");
throw proto_option_error("bad dev-type");
}
// cipher
@ -318,7 +304,7 @@ namespace openvpn {
const std::string meth_name = (*o)[1];
CompressContext::Type meth = CompressContext::parse_method(meth_name);
if (meth == CompressContext::NONE)
OPENVPN_THROW(option_error, "Unknown compressor: '" << meth_name << '\'');
OPENVPN_THROW(proto_option_error, "Unknown compressor: '" << meth_name << '\'');
comp_ctx = CompressContext(meth);
}
else
@ -335,6 +321,7 @@ namespace openvpn {
}
}
// load options string pushed by server
void process_push(const OptionList& opt)
{
// cipher
@ -402,6 +389,24 @@ namespace openvpn {
}
}
void remote_adjust(const RemoteList::Item& rli)
{
// adjust options for new transport protocol
protocol = rli.transport_protocol;
if (protocol.is_udp())
pid_mode = PacketIDReceive::UDP_MODE;
else if (protocol.is_tcp())
pid_mode = PacketIDReceive::TCP_MODE;
else
throw proto_option_error("transport protocol undefined");
}
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() const
@ -1605,6 +1610,9 @@ namespace openvpn {
{
const Config& c = *config;
// validate options
c.validate_complete();
// by default, fast_transition is turned off
fast_transition = false;

View File

@ -1,7 +1,13 @@
#ifndef OPENVPN_TRANSPORT_PROTOCOL_H
#define OPENVPN_TRANSPORT_PROTOCOL_H
#include <string>
#include <boost/cstdint.hpp> // for boost::uint32_t, etc.
#include <boost/algorithm/string.hpp> // for boost::algorithm::to_lower
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
class Protocol
@ -19,6 +25,8 @@ namespace openvpn {
explicit Protocol(const Type t) : type_(t) {}
Type operator()() const { return type_; }
bool defined() const { return type_ != NONE; }
bool is_udp() const { return type_ == UDPv4 || type_ == UDPv6; }
bool is_tcp() const { return type_ == TCPv4 || type_ == TCPv6; }
@ -29,6 +37,35 @@ namespace openvpn {
return is_tcp() ? sizeof(boost::uint16_t) : 0;
}
static Protocol parse(const std::string& str)
{
Protocol ret;
std::string s = str;
boost::algorithm::to_lower(s);
if (s.length() >= 3)
{
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.type_ = UDPv4;
else if (s1 == "tcp")
ret.type_ = TCPv4;
}
else if (s2 == "6" || s2 == "v6")
{
if (s1 == "udp")
ret.type_ = UDPv6;
else if (s1 == "tcp")
ret.type_ = TCPv6;
}
}
if (ret.type_ == NONE)
OPENVPN_THROW(option_error, "error parsing protocol: " << s);
return ret;
}
const char *str() const
{
switch (type_)