OpenVPN 3 Core Library
Loading...
Searching...
No Matches
cmdagent.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#pragma once
13
14#include <utility>
15
30#include <openvpn/win/event.hpp>
32
33namespace openvpn {
34
36{
37 public:
39
41
43 {
44 return new WinCommandAgent(opt);
45 }
46
47 static bool add_bypass_route(IP::Addr endpoint)
48 {
49 std::ostringstream os;
50 os << "WinCommandAgent: transmitting bypass route to " << endpoint.to_string() << std::endl;
51
52 // Build JSON request
53 Json::Value jreq(Json::objectValue);
54
55 jreq["host"] = endpoint.to_string();
56 jreq["ipv6"] = endpoint.is_ipv6();
57 const std::string jtxt = jreq.toStyledString();
58 os << jtxt; // dump it
59
60 OPENVPN_LOG(os.str());
61
62 // Create HTTP transaction container
64
65 SetupClient::make_transaction("add-bypass-route", jtxt, ts);
66
67 // Execute transaction
69
70 return ts->http_status_success();
71 }
72
73 private:
74 struct Config : public RC<thread_unsafe_refcount>
75 {
77
78 std::string npserv = Agent::named_pipe_path(); // server pipe
79 std::string client_exe = Win::module_name_utf8(); // for validation
80 int debug_level = 1;
83 };
84
86 {
87 public:
88 SetupClient(openvpn_io::io_context &io_context,
89 const Config::Ptr &config_arg)
90 : config(config_arg),
91 service_process(io_context)
92 {
93 }
94
95 template <class T>
97 int debug_level,
98 const std::string &client_exe,
99 T cb)
100 {
102 hc->frame = frame_init_simple(2048);
103 hc->connect_timeout = 30;
104 hc->general_timeout = 60;
105
107 ts->host.host = host;
108 ts->host.port = "np";
109 ts->http_config = hc;
110 ts->debug_level = debug_level;
111
112 ts->post_connect = [host, client_exe, cb = std::move(cb)](WS::ClientSet::TransactionSet &ts, AsioPolySock::Base &sock)
113 {
114 AsioPolySock::NamedPipe *np = dynamic_cast<AsioPolySock::NamedPipe *>(&sock);
115 if (np)
116 {
117 Win::NamedPipePeerInfoServer npinfo(np->handle.native_handle());
118 const std::string server_exe = wstring::to_utf8(npinfo.exe_path);
119 if (!Agent::valid_pipe(client_exe, server_exe))
120 OPENVPN_THROW(ovpnagent, host << " server running from " << server_exe << " could not be validated");
121 cb(npinfo.proc.release());
122 }
123 };
124 return ts;
125 }
126
127 static void make_transaction(const std::string &method, const std::string &content, WS::ClientSet::TransactionSet::Ptr ts)
128 {
129 std::unique_ptr<WS::ClientSet::Transaction> t(new WS::ClientSet::Transaction);
130 t->req.method = "POST";
131 t->req.uri = "/" + method;
132 t->ci.type = "application/json";
133 t->content_out.push_back(buf_from_string(content));
134 ts->transactions.push_back(std::move(t));
135 }
136
137 private:
138 HANDLE get_handle(std::ostream &os) override
139 {
140 // Build JSON request
141 Json::Value jreq(Json::objectValue);
142 jreq["confirm_event"] = confirm_event.duplicate_local();
143 jreq["destroy_event"] = destroy_event.duplicate_local();
144 jreq["allow_local_dns_resolvers"] = config->allow_local_dns_resolvers;
145 const std::string jtxt = jreq.toStyledString();
146 os << jtxt; // dump it
147
149 config->npserv,
150 config->debug_level,
151 config->client_exe,
152 [this](HANDLE handle)
153 {
154 if (!service_process.is_open())
155 service_process.assign(handle); });
156
157 make_transaction("tun-open", jtxt, ts);
158
159 // Execute transaction
161
162 // Get result
163 const Json::Value jres = get_json_result(os, *ts);
164
165 // Dump log
166 const std::string log_txt = json::get_string(jres, "log_txt");
167 os << log_txt;
168
169 // Parse TAP handle
170 const std::string tap_handle_hex = json::get_string(jres, "tap_handle_hex");
171 os << "TAP handle: " << tap_handle_hex << std::endl;
172 const HANDLE h = BufHex::parse<HANDLE>(tap_handle_hex, "TAP handle");
173
174 tap_.guid = json::get_string(jres, "adapter_guid");
175 tap_.index = (DWORD)json::get_int(jres, "adapter_index");
176 tap_.name = json::get_string(jres, "adapter_name");
177
178 return h;
179 }
180
182
184 {
185 tap_ = tap;
186 }
187
189 {
190 return tap_;
191 }
192
193 HANDLE establish(const TunBuilderCapture &pull,
194 const std::wstring &openvpn_app_path,
195 Stop *stop,
196 std::ostream &os,
197 TunWin::RingBuffer::Ptr ring_buffer) override // TunWin::SetupBase
198 {
199 os << "SetupClient: transmitting tun setup list to " << config->npserv << std::endl;
200
201 // Build JSON request
202 Json::Value jreq(Json::objectValue);
203
204 if (ring_buffer)
205 ring_buffer->serialize(jreq);
206
207 jreq["destroy_event"] = destroy_event.duplicate_local();
208 if (config->tun_type != TunWin::OvpnDco)
209 {
210 jreq["confirm_event"] = confirm_event.duplicate_local();
211 }
212 else
213 {
214 jreq["adapter_guid"] = tap_.guid;
215 jreq["adapter_index"] = Json::Int(tap_.index);
216 jreq["adapter_name"] = tap_.name;
217 }
218
219 jreq["allow_local_dns_resolvers"] = config->allow_local_dns_resolvers;
220 jreq["tun_type"] = config->tun_type;
221 jreq["tun"] = pull.to_json(); // convert TunBuilderCapture to JSON
222 const std::string jtxt = jreq.toStyledString();
223 os << jtxt; // dump it
224
225 // Create HTTP transaction container
227 config->debug_level,
228 config->client_exe,
229 [this](HANDLE handle)
230 {
231 if (!service_process.is_open())
232 service_process.assign(handle); });
233
234 make_transaction("tun-setup", jtxt, ts);
235
236 // Execute transaction
238
239 // Get result
240 const Json::Value jres = get_json_result(os, *ts);
241
242 // Dump log
243 const std::string log_txt = json::get_string(jres, "log_txt");
244 os << log_txt;
245
246 // Parse TAP handle
247 const std::string tap_handle_hex = json::get_string(jres, "tap_handle_hex");
248 os << "TAP handle: " << tap_handle_hex << std::endl;
249 const HANDLE tap = BufHex::parse<HANDLE>(tap_handle_hex, "TAP handle");
250 return tap;
251 }
252
253 void l2_finish(const TunBuilderCapture &pull,
254 Stop *stop,
255 std::ostream &os) override
256 {
257 throw ovpnagent("l2_finish not implemented");
258 }
259
260 bool l2_ready(const TunBuilderCapture &pull) override
261 {
262 throw ovpnagent("l2_ready not implemented");
263 }
264
265 void confirm() override
266 {
268 }
269
270 void set_service_fail_handler(std::function<void()> &&handler) override
271 {
272 if (service_process.is_open())
273 {
274 service_process.async_wait([handler = std::move(handler)](const openvpn_io::error_code &error)
275 {
276 if (!error)
277 handler(); });
278 }
279 }
280
281 void destroy(std::ostream &os) override // defined by DestructorBase
282 {
283 os << "SetupClient: signaling tun destroy event" << std::endl;
284 service_process.close();
286 }
287
288 Json::Value get_json_result(std::ostream &os, WS::ClientSet::TransactionSet &ts)
289 {
290 // Get content
291 if (ts.transactions.size() != 1)
292 throw ovpnagent("unexpected transaction set size");
294 const std::string content = t.content_in.to_string();
295 os << t.format_status(ts) << std::endl;
296
297 if (t.comm_status_timeout())
298 {
299 // this could be the case when agent service
300 // hasn't been started yet, so we throw a non-fatal
301 // exception which makes core retry.
302 os << "connection timeout";
304 }
305
306 if (!t.comm_status_success())
307 {
308 os << content;
309 throw ovpnagent("communication error");
310 }
311 if (!t.request_status_success())
312 {
313 os << content;
314 throw ovpnagent("request error");
315 }
316
317 // Verify content-type
318 if (t.reply.headers.get_value_trim("content-type") != "application/json")
319 {
320 os << content;
321 throw ovpnagent("unexpected content-type");
322 }
323
324 // Parse the returned json dict
325 Json::CharReaderBuilder builder;
326 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
327 Json::Value jres;
328 std::string err;
329 if (!reader->parse(content.c_str(), content.c_str() + content.size(), &jres, &err))
330 {
331 os << content;
332 OPENVPN_THROW(ovpnagent, "error parsing returned JSON: " << err);
333 }
334 return jres;
335 }
336
338 openvpn_io::windows::object_handle service_process;
341 };
342
343 TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context &io_context, TunWin::Type tun_type, bool allow_local_dns_resolvers) override
344 {
345 if (config)
346 {
347 config->tun_type = tun_type;
348 config->allow_local_dns_resolvers = allow_local_dns_resolvers;
349 return new SetupClient(io_context, config);
350 }
351 else
352 return new TunWin::Setup(io_context, tun_type, allow_local_dns_resolvers);
353 }
354
355 WinCommandAgent(const OptionList &opt_parent)
356 {
357 config.reset(new Config);
358 }
359
361};
362} // namespace openvpn
static std::string named_pipe_path()
static bool valid_pipe(const std::string &client_exe, const std::string &server_exe)
std::string to_string() const
Definition ip.hpp:528
bool is_ipv6() const
Definition ip.hpp:949
The smart pointer class.
Definition rc.hpp:119
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:912
static void new_request_synchronous(const TransactionSet::Ptr ts, Stop *stop=nullptr, RandomAPI *prng=nullptr, const bool sps=false)
openvpn_io::windows::object_handle service_process
Definition cmdagent.hpp:338
TunWin::Util::TapNameGuidPair tap_
Definition cmdagent.hpp:181
void destroy(std::ostream &os) override
Definition cmdagent.hpp:281
void l2_finish(const TunBuilderCapture &pull, Stop *stop, std::ostream &os) override
Definition cmdagent.hpp:253
static WS::ClientSet::TransactionSet::Ptr new_transaction_set(const std::string &host, int debug_level, const std::string &client_exe, T cb)
Definition cmdagent.hpp:96
TunWin::Util::TapNameGuidPair get_adapter_state() override
Definition cmdagent.hpp:188
SetupClient(openvpn_io::io_context &io_context, const Config::Ptr &config_arg)
Definition cmdagent.hpp:88
HANDLE establish(const TunBuilderCapture &pull, const std::wstring &openvpn_app_path, Stop *stop, std::ostream &os, TunWin::RingBuffer::Ptr ring_buffer) override
Definition cmdagent.hpp:193
void set_adapter_state(const TunWin::Util::TapNameGuidPair &tap) override
Definition cmdagent.hpp:183
static void make_transaction(const std::string &method, const std::string &content, WS::ClientSet::TransactionSet::Ptr ts)
Definition cmdagent.hpp:127
bool l2_ready(const TunBuilderCapture &pull) override
Definition cmdagent.hpp:260
void set_service_fail_handler(std::function< void()> &&handler) override
Definition cmdagent.hpp:270
Json::Value get_json_result(std::ostream &os, WS::ClientSet::TransactionSet &ts)
Definition cmdagent.hpp:288
HANDLE get_handle(std::ostream &os) override
Definition cmdagent.hpp:138
TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context &io_context, TunWin::Type tun_type, bool allow_local_dns_resolvers) override
Definition cmdagent.hpp:343
RCPtr< WinCommandAgent > Ptr
Definition cmdagent.hpp:38
static bool add_bypass_route(IP::Addr endpoint)
Definition cmdagent.hpp:47
static TunWin::SetupFactory::Ptr new_agent(const OptionList &opt)
Definition cmdagent.hpp:42
WinCommandAgent(const OptionList &opt_parent)
Definition cmdagent.hpp:355
std::string duplicate_local()
Definition event.hpp:40
void signal_event()
Definition event.hpp:57
static void handler(int signum)
Definition cli.cpp:961
#define OPENVPN_THROW(exc, stuff)
#define OPENVPN_LOG(args)
int ovpnagent(const char *sock_fn, const char *log_fn, const bool log_append, const char *pid_fn, const char *user, const char *group)
std::string module_name_utf8()
Definition modname.hpp:41
int get_int(const Json::Value &root, const NAME &name, const TITLE &title)
std::string get_string(const Json::Value &root, const NAME &name, const TITLE &title)
Frame::Ptr frame_init_simple(const size_t payload)
BufferPtr buf_from_string(const std::string &str)
Definition bufstr.hpp:46
std::string to_string() const
Definition buflist.hpp:72
std::string get_value_trim(const std::string &key) const
Definition header.hpp:84
HeaderList headers
Definition reply.hpp:59
std::string format_status(const TransactionSet &ts) const
proxy_host_port host
std::ostringstream os