25#define HTTP_SERVER_VERSION OPENVPN_STRINGIZE(VERSION)
27#define HTTP_SERVER_VERSION "0.1.0"
33#define OVPNAGENT_NAME_STRING OPENVPN_STRINGIZE(OVPNAGENT_NAME)
35#define OVPNAGENT_NAME_STRING "ovpnagent"
77#ifdef OPENVPN_AGENT_START_PROCESS
94 void error(
const size_t err_type,
const std::string *text =
nullptr)
override
101 std::ostringstream
os;
102 os <<
"OpenVPN Agent Stats" << std::endl;
128 bool allow_local_dns_resolvers)
137 return tun->get_adapter_state();
141 const std::wstring &openvpn_app_path,
142 DWORD client_process_id,
146 bool allow_local_dns_resolvers,
153 tun->set_adapter_state(tap);
155 tun->set_process_id(client_process_id);
178 local_tap_handle.
ref(),
181 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
183 os <<
"destroy_tun: no client confirm, DuplicateHandle (close) succeeded" << std::endl;
188 os <<
"destroy_tun: no client confirm, DuplicateHandle (close) failed: " << err.message() << std::endl;
192 catch (
const std::exception &e)
194 os <<
"destroy_tun: exception in remote tap handle close: " << e.what() << std::endl;
208 catch (
const std::exception &e)
210 os <<
"destroy_tun: exception in tun teardown: " << e.what() << std::endl;
221 catch (
const std::exception &e)
223 os <<
"destroy_tun: exception in cleanup: " << e.what() << std::endl;
231 std::ostringstream
os;
243 client_process.async_wait([self =
Ptr(
this)](
const openvpn_io::error_code &error)
248 std::ostringstream
os;
249 self->remove_cmds_bypass_hosts.execute(
os);
250 self->remove_cmds_bypass_hosts.clear();
256 std::ostringstream
os;
257 self->destroy_tun(
os);
267 const HANDLE remote_event = BufHex::parse<HANDLE>(confirm_handle_hex,
"confirm event handle");
275 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
292 throw Exception(
"set_client_confirm_event: confirm event is abandoned");
307 self->remote_tap_handle_hex.clear();
318 const HANDLE remote_event = BufHex::parse<HANDLE>(event_handle_hex,
"destroy event handle");
326 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
342 std::ostringstream
os;
343 self->remove_cmds_bypass_hosts.execute(
os);
344 self->remove_cmds_bypass_hosts.clear();
350 std::ostringstream
os;
351 self->destroy_tun(
os);
381 std::ostringstream
os;
388 ADDRESS_FAMILY af =
ipv6 ? AF_INET6 : AF_INET;
396#ifdef OPENVPN_AGENT_START_PROCESS
397 void start_openvpn_process(HANDLE client_pipe,
398 const std::string &config_file,
399 const std::string &config_dir,
400 const std::string &exit_event_name,
401 const std::string &management_host,
402 const std::string &management_password,
403 const int management_port,
404 const std::string &log,
405 const bool log_append)
412 HANDLE imp_token = NULL, pri_token = NULL;
413 BOOL res = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, FALSE, &imp_token);
416 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
419 res = DuplicateTokenEx(imp_token, 0, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityAnonymous, TokenPrimary, &pri_token);
422 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
427 HANDLE stdin_read = NULL, stdin_write = NULL;
428 SECURITY_ATTRIBUTES inheritable = {
sizeof(inheritable), NULL, TRUE};
429 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
430 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
432 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
437 std::ostringstream ss;
438 ss <<
"client --config \"" << config_dir <<
"\\" << config_file <<
"\" --exit-event-name "
439 << exit_event_name <<
" --auth-retry interact --management " << management_host <<
" "
440 << management_port <<
" stdin --management-query-passwords --management-hold "
442 << (log_append ?
"-append \"" :
" \"") << log <<
"\"";
443 std::string cmd = ss.str();
444 std::unique_ptr<char[]> buf(
new char[cmd.length() + 1]);
445 strcpy(buf.get(), cmd.c_str());
449 STARTUPINFO startup_info = {0};
450 startup_info.cb =
sizeof(startup_info);
451 startup_info.dwFlags = STARTF_USESTDHANDLES;
452 startup_info.hStdInput = stdin_read;
455 PROCESS_INFORMATION proc_info;
456 ZeroMemory(&proc_info,
sizeof(proc_info));
457 res = CreateProcessAsUser(pri_token,
463 CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
470 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
473 CloseHandle(proc_info.hProcess);
474 CloseHandle(proc_info.hThread);
478 if (!is_safe_conversion<DWORD>(management_password.length()))
480 WriteFile(stdin_write, management_password.c_str(),
static_cast<DWORD
>(management_password.length()), &written, NULL);
493 AsioPolySock::NamedPipe *np =
dynamic_cast<AsioPolySock::NamedPipe *
>(&sock);
497 const std::string client_exe = wstring::to_utf8(npinfo.
exe_path);
501 OPENVPN_LOG(client_exe <<
" not recognized as a valid client");
504 OPENVPN_LOG(
"only named pipe clients are allowed");
540 ci.
type =
"application/json";
549 std::ostringstream
os;
558 OPENVPN_LOG(
"HTTP request received from " <<
sock->remote_endpoint_str() <<
'\n'
572 if (!root.isObject())
573 throw Exception(
"json parse error: top level json object is not a dictionary");
575 if (req.
uri ==
"/tun-open")
580 os <<
"Destroyed previous TAP instance" << std::endl;
587 const std::string confirm_event_hex =
json::get_string(root,
"confirm_event");
588 const std::string destroy_event_hex =
json::get_string(root,
"destroy_event");
607 Json::Value jout(Json::objectValue);
608 jout[
"log_txt"] = log_txt;
612 jout[
"adapter_guid"] = tap.
guid;
613 jout[
"adapter_index"] = Json::Int(tap.index);
614 jout[
"adapter_name"] = tap.name;
621 else if (req.
uri ==
"/tun-setup")
649 os <<
"Destroyed previous TAP instance" << std::endl;
661 const std::string destroy_event_hex =
json::get_string(root,
"destroy_event");
667 const std::string confirm_event_hex =
json::get_string(root,
"confirm_event");
693 Win::ScopedHANDLE tap_handle(
parent()->establish_tun(*
tbc, client_exe, client_pid,
nullptr,
os, tun_type, allow_local_dns_resolvers, tap));
705 Json::Value jout(Json::objectValue);
706 jout[
"log_txt"] = log_txt;
713 else if (req.
uri ==
"/add-bypass-route")
729 Json::Value jout(Json::objectValue);
733#ifdef OPENVPN_AGENT_START_PROCESS
734 else if (req.
uri ==
"/start")
738 const std::string exit_event_name =
json::get_string(root,
"exit_event_name");
739 const std::string management_host =
json::get_string(root,
"management_host");
740 const std::string management_password =
json::get_string(root,
"management_password") +
"\n";
741 const int management_port =
json::get_int(root,
"management_port");
743 const bool log_append =
json::get_int(root,
"log-append") == 1;
745 parent()->start_openvpn_process(client_pipe,
755 Json::Value jout(Json::objectValue);
765 ci.
type =
"text/plain";
771 catch (
const std::exception &e)
774 os <<
"Destroyed previous TAP instance due to exception" << std::endl;
783 ci.
type =
"text/plain";
808 bool http_stop(
const int status,
const std::string &description)
override
821 AsioPolySock::NamedPipe *np =
dynamic_cast<AsioPolySock::NamedPipe *
>(
sock.
get());
823 throw Exception(
"only named pipe clients are allowed");
824 return np->handle.native_handle();
848 throw Exception(
"cannot determine client PID");
888 catch (
const std::exception &e)
890 std::cerr << e.what() << std::endl;
896 io_context.reset(
new openvpn_io::io_context(1));
911 hconf->sddl_string =
"D:"
912 "(D;OICI;GA;;;S-1-5-2)"
913 "(A;OICI;GA;;;S-1-5-32-544)"
914 "(A;OICI;GA;;;S-1-5-18)"
915 "(D;OICI;0x4;;;S-1-1-0)"
916 "(A;OICI;GRGW;;;S-1-5-11)"
917 "(A;OICI;GRGW;;;S-1-5-32-546)"
921 const unsigned int n_pipe_instances = 4;
922 for (
unsigned int i = 0; i < n_pipe_instances; ++i)
930 ll.push_back(std::move(li));
970 const std::string fn =
path::join(moddir,
"agent.log");
981int main(
int argc,
char *argv[])
993 const std::string arg = argv[1];
996 else if (arg ==
"install")
998 else if (arg ==
"remove")
1000 else if (arg ==
"modname")
1002 else if (arg ==
"help")
1004 std::cout <<
"usage: ovpnagent [options]" << std::endl;
1005 std::cout <<
" run -- run in foreground (for debugging)" << std::endl;
1006 std::cout <<
" install -- install as service" << std::endl;
1007 std::cout <<
" remove -- uninstall" << std::endl;
1008 std::cout <<
" modname -- show module name" << std::endl;
1009 std::cout <<
" help -- show help message" << std::endl;
1010 std::cout <<
" [default] -- start as service" << std::endl;
1014 std::cout <<
"unrecognized option, use 'help' for more info" << std::endl;
1021 catch (
const std::exception &e)
1023 std::cout <<
"ovpnagent: " << e.what() << std::endl;
WS::Server::Listener::Client::Ptr new_client(WS::Server::Listener::Client::Initializer &ci) override
RCPtr< MyClientFactory > Ptr
DWORD get_client_pid(const HANDLE client_pipe)
Get the named pipe client process id.
void http_content_in(BufferAllocated &buf) override
bool http_stop(const int status, const std::string &description) override
void generate_reply(const Json::Value &jout)
MyClientInstance(WS::Server::Listener::Client::Initializer &ci)
bool http_out_eof() override
void http_request_received() override
BufferPtr http_content_out() override
std::wstring get_client_exe(const HANDLE client_pipe)
HANDLE get_client_pipe() const
virtual ~MyClientInstance()=default
RCPtr< MyClientInstance > Ptr
Win::ScopedHANDLE get_client_process(const HANDLE pipe, ULONG pid_hint) const
MyListener(const MyConfig &config_arg, openvpn_io::io_context &io_context, const WS::Server::Config::Ptr &hconf, const Listen::List &listen_list, const WS::Server::Listener::Client::Factory::Ptr &client_factory)
void set_client_destroy_event(const std::string &event_handle_hex)
bool allow_client(AsioPolySock::Base &sock) override
TunWin::Util::TapNameGuidPair get_adapter_state()
Win::ScopedHANDLE tun_get_handle(std::ostream &os, const TunWin::Type tun_type, bool allow_local_dns_resolvers)
Win::ScopedHANDLE establish_tun(const TunBuilderCapture &tbc, const std::wstring &openvpn_app_path, DWORD client_process_id, Stop *stop, std::ostream &os, TunWin::Type tun_type, bool allow_local_dns_resolvers, TunWin::Util::TapNameGuidPair tap)
const std::string & get_remote_tap_handle_hex()
void set_client_confirm_event(const std::string &confirm_handle_hex)
void set_remote_tap_handle_hex(const HANDLE tap_handle)
ActionList remove_cmds_bypass_hosts
DWORD vpn_interface_index
openvpn_io::io_context & io_context_
openvpn_io::windows::object_handle client_confirm_event
void set_client_process(Win::ScopedHANDLE &&proc)
bool destroy_tun(std::ostream &os)
void destroy_tun(std::ostream &os)
void add_bypass_route(const std::string &host, bool ipv6)
HANDLE get_client_process()
std::string remote_tap_handle_hex
TunWin::RingBuffer::Ptr ring_buffer
openvpn_io::windows::object_handle client_process
void assign_ring_buffer(TunWin::RingBuffer *ring_buffer_arg)
CancelableHandle client_destroy_event
void service_stop() override
void service_work(DWORD argc, LPWSTR *argv) override
std::unique_ptr< openvpn_io::io_context > io_context
static std::string log_fn()
void error(const size_t err_type, const std::string *text=nullptr) override
RCPtr< MySessionStats > Ptr
virtual std::unordered_set< std::string > execute(std::ostream &os)
Executes a sequence of actions and returns marks of failed actions.
static std::string named_pipe_path()
static bool valid_pipe(const std::string &client_exe, const std::string &server_exe)
Wrapper for an asynchronous handle supporting cancellation and closure.
void check_is_already_signalled()
Checks if the handle's event is already signaled.
void async_wait(Handler &&handler)
Initiates an asynchronous wait on the handle.
void cancel_and_close()
Cancels and closes the handle if not already closed.
void assign(HANDLE handle)
Assigns a native Windows handle.
bool defined() const
Returns true if the buffer is not empty.
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
void swap(RCPtr &rhs) noexcept
swaps the contents of two RCPtr<T>
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
void validate() const
Validates the configuration of the tunnel.
static TunBuilderCapture::Ptr from_json(const Json::Value &root)
Creates a TunBuilderCapture instance from a JSON representation.
static void delete_rules(DWORD process_id)
Remove our NRPT rules from the registry.
static void add_bypass_route(const Util::BestGateway &gw, const std::string &route, bool ipv6, ActionList &add_cmds, ActionList &remove_cmds_bypass_gw)
void generate_reply_headers(ContentInfo ci)
AsioPolySock::Base::Ptr sock
Listener * get_parent() const
const HTTP::Request & request() const
openvpn_io::io_context & io_context
openvpn_io::io_context & io_context
Listener(openvpn_io::io_context &io_context_arg, const Config::Ptr &config_arg, const L &listen_item_or_list, const Client::Factory::Ptr &client_factory_arg)
Client::Factory::Ptr client_factory
void report_service_running()
#define OPENVPN_SIMPLE_EXCEPTION(C)
#define OPENVPN_THROW_EXCEPTION(stuff)
#define OPENVPN_LOG_NTNL(args)
#define OPENVPN_LOG(args)
#define OPENVPN_LOG_STRING(str)
const char * name(const size_t type)
std::string send_handle(const HANDLE handle, const HANDLE remote_process)
std::string module_name_utf8()
std::string omiclient_path()
std::wstring module_name()
int get_int(const Json::Value &root, const NAME &name, const TITLE &title)
bool get_bool_optional(const Json::Value &root, const NAME &name, const bool default_value=false)
bool get_bool(const Json::Value &root, const NAME &name, const TITLE &title)
unsigned int get_uint_optional(const Json::Value &root, const NAME &name, const unsigned int default_value, const TITLE &title)
const Json::Value & get_dict(const Json::Value &root, const NAME &name, const bool optional, const TITLE &title)
int get_int_optional(const Json::Value &root, const NAME &name, const int default_value, const TITLE &title)
Json::Value parse(const std::string &str, const TITLE &title)
std::string get_string(const Json::Value &root, const NAME &name, const TITLE &title)
std::string dirname(const std::string &path)
std::string join(const std::string &p1, const std::string &p2)
std::string remove_blanks(const std::string &str)
int strcasecmp(const char *s1, const char *s2)
Frame::Ptr frame_init_simple(const size_t payload)
BufferPtr buf_from_string(const std::string &str)
unsigned int n_pipe_instances
std::string omiclient_exe
std::string to_string() const
std::string to_string() const
static std::string error_str(const size_t status)
static Win::ScopedHANDLE get_process(const ULONG pid, const bool limited)
static ULONG get_pid(const HANDLE np_handle, const bool client)
static void allow_client_query()
const TunBuilderCapture::Ptr tbc(new TunBuilderCapture)
int main(int argc, char *argv[])
#define HTTP_SERVER_VERSION
#define OVPNAGENT_NAME_STRING