mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
First working OMI wrapper for OpenVPN 3 client. Tested with OpenVPN Connect on Mac.
Could benefit from some minor modifications to python client backend and tray app: * Client backend and ovpn3 both implement connection timeout. Client backend should defer to the ovpn3 implementation. * Client backend and ovpn3 both implement DNS server config and SystemConfiguration event sent to 'Setup:/Network/Global/IPv4' for 'VPN up'. Client backend should defer to the ovpn3 implementation. * Ensure that system state changes (sleep, wakeup, network roam, fast user switching, etc.) don't cause conflicts between client backend and ovpn3 core both trying to implement similar functionality. * Tray app should render error detail in >FATAL: messages. Right now tray raises a Disconnected notification but loses any error detail.
This commit is contained in:
parent
e8a21acb25
commit
d0c63d3150
@ -28,11 +28,14 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <openvpn/common/platform.hpp>
|
#include <openvpn/common/platform.hpp>
|
||||||
#include <openvpn/common/exception.hpp>
|
#include <openvpn/common/exception.hpp>
|
||||||
#include <openvpn/common/rc.hpp>
|
#include <openvpn/common/rc.hpp>
|
||||||
#include <openvpn/common/string.hpp>
|
#include <openvpn/common/string.hpp>
|
||||||
|
#include <openvpn/common/number.hpp>
|
||||||
|
#include <openvpn/common/hostport.hpp>
|
||||||
#include <openvpn/common/options.hpp>
|
#include <openvpn/common/options.hpp>
|
||||||
#include <openvpn/buffer/bufstr.hpp>
|
#include <openvpn/buffer/bufstr.hpp>
|
||||||
|
|
||||||
@ -49,7 +52,26 @@ namespace openvpn {
|
|||||||
public:
|
public:
|
||||||
OPENVPN_EXCEPTION(omi_error);
|
OPENVPN_EXCEPTION(omi_error);
|
||||||
|
|
||||||
struct Command {
|
void stop()
|
||||||
|
{
|
||||||
|
if (stop_called)
|
||||||
|
return;
|
||||||
|
stop_called = true;
|
||||||
|
|
||||||
|
// close acceptor
|
||||||
|
if (acceptor)
|
||||||
|
acceptor->close();
|
||||||
|
|
||||||
|
// Call derived class stop method and close OMI socket,
|
||||||
|
// but if omi_stop() returns true, wait for content_out
|
||||||
|
// to be flushed to OMI socket before closing it.
|
||||||
|
if (!omi_stop() || content_out.empty())
|
||||||
|
stop_omi_client(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct Command
|
||||||
|
{
|
||||||
Option option;
|
Option option;
|
||||||
std::vector<std::string> extra;
|
std::vector<std::string> extra;
|
||||||
bool valid_utf8 = false;
|
bool valid_utf8 = false;
|
||||||
@ -67,6 +89,113 @@ namespace openvpn {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class History
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
History(const std::string& type_arg,
|
||||||
|
const size_t max_size_arg)
|
||||||
|
: type(type_arg),
|
||||||
|
max_size(max_size_arg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_cmd(const Option& o) const
|
||||||
|
{
|
||||||
|
return o.get_optional(0, 0) == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string process_cmd(const Option& o)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const std::string arg1 = o.get(1, 16);
|
||||||
|
if (arg1 == "on")
|
||||||
|
{
|
||||||
|
const std::string arg2 = o.get_optional(2, 16);
|
||||||
|
real_time = true;
|
||||||
|
std::string ret = real_time_status();
|
||||||
|
if (arg2 == "all")
|
||||||
|
ret += show(hist.size());
|
||||||
|
else if (!arg2.empty())
|
||||||
|
return error();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else if (arg1 == "all")
|
||||||
|
{
|
||||||
|
return show(hist.size());
|
||||||
|
}
|
||||||
|
else if (arg1 == "off")
|
||||||
|
{
|
||||||
|
real_time = false;
|
||||||
|
return real_time_status();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
if (parse_number(arg1, n))
|
||||||
|
return show(n);
|
||||||
|
else
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const option_error&)
|
||||||
|
{
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
return "ERROR: " + type + " processing error: " + e.what() + "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string notify(const std::string& msg)
|
||||||
|
{
|
||||||
|
hist.push_front(msg);
|
||||||
|
while (hist.size() > max_size)
|
||||||
|
hist.pop_back();
|
||||||
|
if (real_time)
|
||||||
|
return notify_prefix() + msg;
|
||||||
|
else
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string show(size_t n) const
|
||||||
|
{
|
||||||
|
std::string ret = "";
|
||||||
|
n = std::min(n, hist.size());
|
||||||
|
for (size_t i = 0; i < n; ++i)
|
||||||
|
ret += hist[n - i - 1];
|
||||||
|
ret += "END\r\n";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string notify_prefix() const
|
||||||
|
{
|
||||||
|
return ">" + string::to_upper_copy(type) + ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string real_time_status() const
|
||||||
|
{
|
||||||
|
std::string ret = "SUCCESS: real-time " + type + " notification set to ";
|
||||||
|
if (real_time)
|
||||||
|
ret += "ON";
|
||||||
|
else
|
||||||
|
ret += "OFF";
|
||||||
|
ret += "\r\n";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error() const
|
||||||
|
{
|
||||||
|
return "ERROR: " + type + " parameter must be 'on' or 'off' or some number n or 'all'\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string type;
|
||||||
|
size_t max_size;
|
||||||
|
bool real_time = false;
|
||||||
|
std::deque<std::string> hist;
|
||||||
|
};
|
||||||
|
|
||||||
OMICore(asio::io_context& io_context_arg,
|
OMICore(asio::io_context& io_context_arg,
|
||||||
OptionList opt_arg)
|
OptionList opt_arg)
|
||||||
: io_context(io_context_arg),
|
: io_context(io_context_arg),
|
||||||
@ -77,9 +206,18 @@ namespace openvpn {
|
|||||||
void open_log()
|
void open_log()
|
||||||
{
|
{
|
||||||
// open log file
|
// open log file
|
||||||
const std::string log_fn = opt.get_optional("log", 1, 256);
|
bool append = false;
|
||||||
|
std::string log_fn = opt.get_optional("log", 1, 256);
|
||||||
|
if (log_fn.empty())
|
||||||
|
{
|
||||||
|
log_fn = opt.get_optional("log-append", 1, 256);
|
||||||
|
append = true;
|
||||||
|
}
|
||||||
if (!log_fn.empty())
|
if (!log_fn.empty())
|
||||||
log_setup(log_fn);
|
log_setup(log_fn, append);
|
||||||
|
|
||||||
|
// other log-related options
|
||||||
|
errors_to_stderr = opt.exists("errors-to-stderr");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_config() const
|
std::string get_config() const
|
||||||
@ -94,6 +232,21 @@ namespace openvpn {
|
|||||||
const Option& o = opt.get("management");
|
const Option& o = opt.get("management");
|
||||||
const std::string addr = o.get(1, 256);
|
const std::string addr = o.get(1, 256);
|
||||||
const std::string port = o.get(2, 16);
|
const std::string port = o.get(2, 16);
|
||||||
|
|
||||||
|
hold_flag = opt.exists("management-hold");
|
||||||
|
|
||||||
|
// management-client-user root
|
||||||
|
{
|
||||||
|
const Option* o = opt.get_ptr("management-client-user");
|
||||||
|
if (o)
|
||||||
|
{
|
||||||
|
if (o->get(1, 64) == "root")
|
||||||
|
management_client_root = true;
|
||||||
|
else
|
||||||
|
throw Exception("only --management-client-user root supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opt.exists("management-client"))
|
if (opt.exists("management-client"))
|
||||||
{
|
{
|
||||||
if (port == "unix")
|
if (port == "unix")
|
||||||
@ -122,24 +275,9 @@ namespace openvpn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
if (halt)
|
|
||||||
return;
|
|
||||||
halt = true;
|
|
||||||
|
|
||||||
// close acceptor
|
|
||||||
if (acceptor)
|
|
||||||
acceptor->close();
|
|
||||||
|
|
||||||
// close client socket
|
|
||||||
stop_client(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void send(BufferPtr buf)
|
void send(BufferPtr buf)
|
||||||
{
|
{
|
||||||
if (halt || !is_sock_open())
|
if (!is_sock_open())
|
||||||
return;
|
return;
|
||||||
content_out.push_back(std::move(buf));
|
content_out.push_back(std::move(buf));
|
||||||
if (content_out.size() == 1) // send operation not currently active?
|
if (content_out.size() == 1) // send operation not currently active?
|
||||||
@ -148,37 +286,260 @@ namespace openvpn {
|
|||||||
|
|
||||||
void send(const std::string& str)
|
void send(const std::string& str)
|
||||||
{
|
{
|
||||||
|
if (!str.empty())
|
||||||
send(buf_from_string(str));
|
send(buf_from_string(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool omi_command_is_multiline(const Option& option) = 0;
|
void log_line(const std::string& line)
|
||||||
virtual void omi_command_in(std::unique_ptr<Command> cmd) = 0;
|
{
|
||||||
|
if (!stop_called)
|
||||||
|
send(hist_log.notify(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_line(const std::string& line)
|
||||||
|
{
|
||||||
|
if (!stop_called)
|
||||||
|
send(hist_state.notify(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
void echo_line(const std::string& line)
|
||||||
|
{
|
||||||
|
if (!stop_called)
|
||||||
|
send(hist_echo.notify(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_errors_to_stderr() const
|
||||||
|
{
|
||||||
|
return errors_to_stderr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_stopping() const
|
||||||
|
{
|
||||||
|
return stop_called;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int get_bytecount() const
|
||||||
|
{
|
||||||
|
return bytecount;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool omi_command_is_multiline(const std::string& arg0, const Option& option) = 0;
|
||||||
|
virtual void omi_command_in(const std::string& arg0, const Command& cmd) = 0;
|
||||||
|
virtual void omi_start_connection() = 0;
|
||||||
virtual void omi_done(const bool eof) = 0;
|
virtual void omi_done(const bool eof) = 0;
|
||||||
|
virtual void omi_sigterm() = 0;
|
||||||
|
virtual void omi_sighup() = 0;
|
||||||
|
virtual bool omi_stop() = 0;
|
||||||
|
|
||||||
asio::io_context& io_context;
|
asio::io_context& io_context;
|
||||||
const OptionList opt;
|
const OptionList opt;
|
||||||
bool halt = false;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef RCPtr<OMICore> Ptr;
|
typedef RCPtr<OMICore> Ptr;
|
||||||
|
|
||||||
|
void command_in(std::unique_ptr<Command> cmd)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const std::string arg0 = cmd->option.get_optional(0, 64);
|
||||||
|
if (arg0.empty())
|
||||||
|
return;
|
||||||
|
if (!cmd->valid_utf8)
|
||||||
|
throw Exception("invalid UTF8");
|
||||||
|
switch (arg0[0])
|
||||||
|
{
|
||||||
|
case 'b':
|
||||||
|
{
|
||||||
|
if (arg0 == "bytecount")
|
||||||
|
{
|
||||||
|
process_bytecount_cmd(cmd->option);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'e':
|
||||||
|
{
|
||||||
|
if (hist_echo.is_cmd(cmd->option))
|
||||||
|
{
|
||||||
|
send(hist_echo.process_cmd(cmd->option));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (arg0 == "exit")
|
||||||
|
{
|
||||||
|
conditional_stop(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'h':
|
||||||
|
{
|
||||||
|
if (is_hold_cmd(cmd->option))
|
||||||
|
{
|
||||||
|
bool release = false;
|
||||||
|
send(hold_cmd(cmd->option, release));
|
||||||
|
if (release)
|
||||||
|
hold_release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'l':
|
||||||
|
{
|
||||||
|
if (hist_log.is_cmd(cmd->option))
|
||||||
|
{
|
||||||
|
send(hist_log.process_cmd(cmd->option));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'q':
|
||||||
|
{
|
||||||
|
if (arg0 == "quit")
|
||||||
|
{
|
||||||
|
conditional_stop(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
if (hist_state.is_cmd(cmd->option))
|
||||||
|
{
|
||||||
|
send(hist_state.process_cmd(cmd->option));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (arg0 == "signal")
|
||||||
|
{
|
||||||
|
process_signal_cmd(cmd->option);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
omi_command_in(arg0, *cmd);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::string err_ref = "option";
|
||||||
|
if (cmd)
|
||||||
|
err_ref = cmd->option.err_ref();
|
||||||
|
send("ERROR: error processing " + err_ref + " : " + e.what() + "\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_hold_cmd(const Option& o) const
|
||||||
|
{
|
||||||
|
return o.get_optional(0, 0) == "hold";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hold_cmd(const Option& o, bool& release)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const std::string arg1 = o.get_optional(1, 16);
|
||||||
|
if (arg1.empty())
|
||||||
|
{
|
||||||
|
if (hold_flag)
|
||||||
|
return "SUCCESS: hold=1\r\n";
|
||||||
|
else
|
||||||
|
return "SUCCESS: hold=0\r\n";
|
||||||
|
}
|
||||||
|
else if (arg1 == "on")
|
||||||
|
{
|
||||||
|
hold_flag = true;
|
||||||
|
return "SUCCESS: hold flag set to ON\r\n";
|
||||||
|
}
|
||||||
|
else if (arg1 == "off")
|
||||||
|
{
|
||||||
|
hold_flag = false;
|
||||||
|
return "SUCCESS: hold flag set to OFF\r\n";
|
||||||
|
}
|
||||||
|
else if (arg1 == "release")
|
||||||
|
{
|
||||||
|
release = true;
|
||||||
|
return "SUCCESS: hold release succeeded\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const option_error&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return "ERROR: bad hold command parameter\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void hold_cycle()
|
||||||
|
{
|
||||||
|
hold_wait = true;
|
||||||
|
if (hold_flag)
|
||||||
|
send(">HOLD:Waiting for hold release\r\n");
|
||||||
|
else
|
||||||
|
hold_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void hold_release()
|
||||||
|
{
|
||||||
|
if (hold_wait)
|
||||||
|
{
|
||||||
|
hold_wait = false;
|
||||||
|
omi_start_connection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_bytecount_cmd(const Option& o)
|
||||||
|
{
|
||||||
|
bytecount = o.get_num<decltype(bytecount)>(1, 0, 0, 86400);
|
||||||
|
send("SUCCESS: bytecount interval changed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_signal_cmd(const Option& o)
|
||||||
|
{
|
||||||
|
const std::string type = o.get(1, 16);
|
||||||
|
if (type == "SIGTERM")
|
||||||
|
{
|
||||||
|
send("SUCCESS: signal SIGTERM thrown\r\n");
|
||||||
|
omi_sigterm();
|
||||||
|
}
|
||||||
|
else if (type == "SIGHUP")
|
||||||
|
{
|
||||||
|
send("SUCCESS: signal SIGHUP thrown\r\n");
|
||||||
|
omi_sighup();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
send("ERROR: signal not supported\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool command_is_multiline(const Option& o)
|
||||||
|
{
|
||||||
|
const std::string arg0 = o.get_optional(0, 64);
|
||||||
|
if (arg0.empty())
|
||||||
|
return false;
|
||||||
|
return omi_command_is_multiline(arg0, o);
|
||||||
|
}
|
||||||
|
|
||||||
bool is_sock_open() const
|
bool is_sock_open() const
|
||||||
{
|
{
|
||||||
return socket && socket->is_open();
|
return socket && socket->is_open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_client(const bool eof)
|
void conditional_stop(const bool eof)
|
||||||
{
|
{
|
||||||
if (is_sock_open())
|
if (acceptor || stop_called)
|
||||||
|
stop_omi_client(eof);
|
||||||
|
else
|
||||||
|
stop(); // if running in management-client mode, do a full stop
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_omi_client(const bool eof)
|
||||||
|
{
|
||||||
|
const bool is_open = is_sock_open();
|
||||||
|
if (is_open)
|
||||||
socket->close();
|
socket->close();
|
||||||
content_out.clear();
|
content_out.clear();
|
||||||
in_partial.clear();
|
in_partial.clear();
|
||||||
|
if (is_open)
|
||||||
omi_done(eof);
|
omi_done(eof);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_title_message()
|
void send_title_message()
|
||||||
{
|
{
|
||||||
send(">INFO:OpenVPN Management Interface Version 1 -- type 'help' for more info\n");
|
send(">INFO:OpenVPN Management Interface Version 1 -- type 'help' for more info\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_in_line() // process incoming line in in_partial
|
void process_in_line() // process incoming line in in_partial
|
||||||
@ -191,7 +552,7 @@ namespace openvpn {
|
|||||||
throw omi_error("process_in_line: internal error");
|
throw omi_error("process_in_line: internal error");
|
||||||
if (in_partial == "END")
|
if (in_partial == "END")
|
||||||
{
|
{
|
||||||
omi_command_in(std::move(command));
|
command_in(std::move(command));
|
||||||
command.reset();
|
command.reset();
|
||||||
multiline = false;
|
multiline = false;
|
||||||
}
|
}
|
||||||
@ -207,10 +568,10 @@ namespace openvpn {
|
|||||||
command.reset(new Command);
|
command.reset(new Command);
|
||||||
command->option = OptionList::parse_option_from_line(in_partial, nullptr);
|
command->option = OptionList::parse_option_from_line(in_partial, nullptr);
|
||||||
command->valid_utf8 = utf8;
|
command->valid_utf8 = utf8;
|
||||||
multiline = omi_command_is_multiline(command->option);
|
multiline = command_is_multiline(command->option);
|
||||||
if (!multiline)
|
if (!multiline)
|
||||||
{
|
{
|
||||||
omi_command_in(std::move(command));
|
command_in(std::move(command));
|
||||||
command.reset();
|
command.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,14 +585,14 @@ namespace openvpn {
|
|||||||
return read_text_utf8(fn);
|
return read_text_utf8(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_setup(const std::string& log_fn)
|
void log_setup(const std::string& log_fn, const bool append)
|
||||||
{
|
{
|
||||||
#if defined(OPENVPN_PLATFORM_WIN)
|
#if defined(OPENVPN_PLATFORM_WIN)
|
||||||
// fixme -- code for Windows
|
// fixme -- code for Windows
|
||||||
#else
|
#else
|
||||||
RedirectStd redir("",
|
RedirectStd redir("",
|
||||||
log_fn,
|
log_fn,
|
||||||
RedirectStd::FLAGS_OVERWRITE,
|
append ? RedirectStd::FLAGS_APPEND : RedirectStd::FLAGS_OVERWRITE,
|
||||||
RedirectStd::MODE_ALL,
|
RedirectStd::MODE_ALL,
|
||||||
false);
|
false);
|
||||||
redir.redirect();
|
redir.redirect();
|
||||||
@ -246,7 +607,7 @@ namespace openvpn {
|
|||||||
// parse address/port of local endpoint
|
// parse address/port of local endpoint
|
||||||
const IP::Addr ip_addr = IP::Addr::from_string(addr);
|
const IP::Addr ip_addr = IP::Addr::from_string(addr);
|
||||||
a->local_endpoint.address(ip_addr.to_asio());
|
a->local_endpoint.address(ip_addr.to_asio());
|
||||||
a->local_endpoint.port(HostPort::parse_port(port, "tcp listen"));
|
a->local_endpoint.port(HostPort::parse_port(port, "OMI TCP listen"));
|
||||||
|
|
||||||
// open socket
|
// open socket
|
||||||
a->acceptor.open(a->local_endpoint.protocol());
|
a->acceptor.open(a->local_endpoint.protocol());
|
||||||
@ -301,45 +662,88 @@ namespace openvpn {
|
|||||||
|
|
||||||
void queue_accept()
|
void queue_accept()
|
||||||
{
|
{
|
||||||
|
if (acceptor)
|
||||||
acceptor->async_accept(this, 0, io_context);
|
acceptor->async_accept(this, 0, io_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void verify_sock_peer(AsioPolySock::Base& sock)
|
||||||
|
{
|
||||||
|
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||||
|
SockOpt::Creds cr;
|
||||||
|
if (management_client_root && sock.peercreds(cr))
|
||||||
|
{
|
||||||
|
if (!cr.root_uid())
|
||||||
|
throw Exception("peer must be root");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// despite its name, this method handles both accept and connect events
|
||||||
virtual void handle_accept(AsioPolySock::Base::Ptr sock, const asio::error_code& error) override
|
virtual void handle_accept(AsioPolySock::Base::Ptr sock, const asio::error_code& error) override
|
||||||
{
|
{
|
||||||
if (halt)
|
if (stop_called)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (error)
|
if (error)
|
||||||
throw Exception("accept failed: " + error.message());
|
throw Exception("accept/connect failed: " + error.message());
|
||||||
if (is_sock_open())
|
if (is_sock_open())
|
||||||
throw Exception("client already connected");
|
throw Exception("client already connected");
|
||||||
|
|
||||||
|
verify_sock_peer(*sock);
|
||||||
|
|
||||||
sock->non_blocking(true);
|
sock->non_blocking(true);
|
||||||
sock->set_cloexec();
|
sock->set_cloexec();
|
||||||
socket = std::move(sock);
|
socket = std::move(sock);
|
||||||
|
|
||||||
send_title_message();
|
send_title_message();
|
||||||
queue_recv();
|
queue_recv();
|
||||||
|
hold_cycle();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
std::cerr << "exception in handle_accept: " << e.what() << std::endl;
|
const std::string msg = "exception in accept/connect handler: " + std::string(e.what()) + '\n';
|
||||||
|
if (errors_to_stderr)
|
||||||
|
std::cerr << msg << std::flush;
|
||||||
|
OPENVPN_LOG_STRING(msg);
|
||||||
}
|
}
|
||||||
queue_accept();
|
queue_accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void connect_tcp(const std::string& addr, const std::string& port)
|
void connect_tcp(const std::string& addr, const std::string& port)
|
||||||
{
|
{
|
||||||
|
asio::ip::tcp::endpoint ep(IP::Addr::from_string(addr).to_asio(),
|
||||||
|
HostPort::parse_port(port, "OMI TCP connect"));
|
||||||
|
AsioPolySock::TCP* s = new AsioPolySock::TCP(io_context, 0);
|
||||||
|
AsioPolySock::Base::Ptr sock(s);
|
||||||
|
s->socket.async_connect(ep,
|
||||||
|
[self=Ptr(this), sock](const asio::error_code& error)
|
||||||
|
{
|
||||||
|
// this is a connect, but we reuse the accept method
|
||||||
|
self->handle_accept(sock, error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void connect_unix(const std::string& socket_path)
|
void connect_unix(const std::string& socket_path)
|
||||||
{
|
{
|
||||||
|
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||||
|
asio::local::stream_protocol::endpoint ep(socket_path);
|
||||||
|
AsioPolySock::Unix* s = new AsioPolySock::Unix(io_context, 0);
|
||||||
|
AsioPolySock::Base::Ptr sock(s);
|
||||||
|
s->socket.async_connect(ep,
|
||||||
|
[self=Ptr(this), sock](const asio::error_code& error)
|
||||||
|
{
|
||||||
|
// this is a connect, but we reuse the accept method
|
||||||
|
self->handle_accept(sock, error);
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
throw Exception("unix sockets not supported on this platform");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void queue_recv()
|
void queue_recv()
|
||||||
{
|
{
|
||||||
if (halt || !is_sock_open())
|
if (!is_sock_open())
|
||||||
return;
|
return;
|
||||||
BufferPtr buf(new BufferAllocated(256, 0));
|
BufferPtr buf(new BufferAllocated(256, 0));
|
||||||
socket->async_receive(buf->mutable_buffers_1_clamp(),
|
socket->async_receive(buf->mutable_buffers_1_clamp(),
|
||||||
@ -352,14 +756,14 @@ namespace openvpn {
|
|||||||
void handle_recv(const asio::error_code& error, const size_t bytes_recvd,
|
void handle_recv(const asio::error_code& error, const size_t bytes_recvd,
|
||||||
Buffer& buf, const AsioPolySock::Base* queued_socket)
|
Buffer& buf, const AsioPolySock::Base* queued_socket)
|
||||||
{
|
{
|
||||||
if (halt || !is_sock_open() || socket.get() != queued_socket)
|
if (!is_sock_open() || socket.get() != queued_socket)
|
||||||
return;
|
return;
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
const bool eof = (error == asio::error::eof);
|
const bool eof = (error == asio::error::eof);
|
||||||
if (!eof)
|
if (!eof)
|
||||||
OPENVPN_LOG("client socket recv error: " << error.message());
|
OPENVPN_LOG("client socket recv error: " << error.message());
|
||||||
stop_client(eof);
|
conditional_stop(eof);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buf.set_size(bytes_recvd);
|
buf.set_size(bytes_recvd);
|
||||||
@ -375,9 +779,7 @@ namespace openvpn {
|
|||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
OPENVPN_LOG("error processing omi command: " << e.what());
|
send("ERROR: in OMI command: " + std::string(e.what()) + "\r\n");
|
||||||
stop_client(false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
in_partial.clear();
|
in_partial.clear();
|
||||||
}
|
}
|
||||||
@ -388,7 +790,7 @@ namespace openvpn {
|
|||||||
|
|
||||||
void queue_send()
|
void queue_send()
|
||||||
{
|
{
|
||||||
if (halt || !is_sock_open())
|
if (!is_sock_open())
|
||||||
return;
|
return;
|
||||||
BufferAllocated& buf = *content_out.front();
|
BufferAllocated& buf = *content_out.front();
|
||||||
socket->async_send(buf.const_buffers_1_clamp(),
|
socket->async_send(buf.const_buffers_1_clamp(),
|
||||||
@ -401,13 +803,13 @@ namespace openvpn {
|
|||||||
void handle_send(const asio::error_code& error, const size_t bytes_sent,
|
void handle_send(const asio::error_code& error, const size_t bytes_sent,
|
||||||
const AsioPolySock::Base* queued_socket)
|
const AsioPolySock::Base* queued_socket)
|
||||||
{
|
{
|
||||||
if (halt || !is_sock_open() || socket.get() != queued_socket)
|
if (!is_sock_open() || socket.get() != queued_socket)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
OPENVPN_LOG("client socket send error: " << error.message());
|
OPENVPN_LOG("client socket send error: " << error.message());
|
||||||
stop_client(false);
|
conditional_stop(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,20 +821,40 @@ namespace openvpn {
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
OPENVPN_LOG("client socket unexpected send size: " << bytes_sent << '/' << buf->size());
|
OPENVPN_LOG("client socket unexpected send size: " << bytes_sent << '/' << buf->size());
|
||||||
stop_client(false);
|
conditional_stop(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content_out.empty())
|
if (!content_out.empty())
|
||||||
queue_send();
|
queue_send();
|
||||||
|
else if (stop_called)
|
||||||
|
conditional_stop(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I/O
|
||||||
Acceptor::Base::Ptr acceptor;
|
Acceptor::Base::Ptr acceptor;
|
||||||
AsioPolySock::Base::Ptr socket;
|
AsioPolySock::Base::Ptr socket;
|
||||||
std::deque<BufferPtr> content_out;
|
std::deque<BufferPtr> content_out;
|
||||||
std::string in_partial;
|
std::string in_partial;
|
||||||
std::unique_ptr<Command> command;
|
std::unique_ptr<Command> command;
|
||||||
|
bool management_client_root = false;
|
||||||
bool multiline = false;
|
bool multiline = false;
|
||||||
|
bool errors_to_stderr = false;
|
||||||
|
|
||||||
|
// stopping
|
||||||
|
bool stop_called = false;
|
||||||
|
|
||||||
|
// hold
|
||||||
|
bool hold_wait = false;
|
||||||
|
bool hold_flag = false;
|
||||||
|
|
||||||
|
// bandwidth stats
|
||||||
|
unsigned int bytecount = 0;
|
||||||
|
|
||||||
|
// histories
|
||||||
|
History hist_log {"log", 100};
|
||||||
|
History hist_state {"state", 100};
|
||||||
|
History hist_echo {"echo", 100};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user