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// Transmit TunBuilderCapture object (as JSON) to a unix
13// domain socket server that will establish tunnel.
14
15#ifndef OPENVPN_CLIENT_UNIX_CMDAGENT_H
16#define OPENVPN_CLIENT_UNIX_CMDAGENT_H
17
18#include <utility>
19
29
30#ifdef OVPNAGENT_NAME
31#define OVPNAGENT_NAME_STRING OPENVPN_STRINGIZE(OVPNAGENT_NAME)
32#else
33#define OVPNAGENT_NAME_STRING "ovpnagent"
34#endif
35
36namespace openvpn {
37
39{
40 public:
42
44
46 {
47 return new UnixCommandAgent(opt);
48 }
49
50 static bool add_bypass_route(IP::Addr endpoint)
51 {
53
54 std::ostringstream os;
55 os << "UnixCommandAgent: transmitting bypass route to " << config.uds_name << std::endl;
56
57 // Build JSON request
58 Json::Value jreq(Json::objectValue);
59 jreq["pid"] = Json::Value(getpid());
60 jreq["host"] = Json::Value(endpoint.to_string());
61 jreq["ipv6"] = Json::Value(endpoint.is_ipv6());
62 const std::string jtxt = jreq.toStyledString();
63 os << jtxt; // dump it
64
65 OPENVPN_LOG(os.str());
66
68 SetupClient::make_transaction("add-bypass-route", jtxt, false, ts);
70
71 return ts->http_status_success();
72 }
73
74 private:
75 struct Config : public RC<thread_unsafe_refcount>
76 {
78
80 {
81 uds_name = "/var/run/" OVPNAGENT_NAME_STRING ".sock";
82 debug_level = 1;
83 }
84
85 std::string uds_name; // unix domain socket name
87 };
88
90 {
91 public:
92 SetupClient(const Config::Ptr &config_arg)
93 : config(config_arg)
94 {
95 }
96
98 int debug_level)
99 {
101 hc->frame = frame_init_simple(2048);
102 hc->connect_timeout = 10;
103 hc->general_timeout = 60;
104
106 ts->host.host = host;
107 ts->host.port = "unix";
108 ts->http_config = hc;
109 ts->debug_level = debug_level;
110
111 ts->post_connect = [host](WS::ClientSet::TransactionSet &ts, AsioPolySock::Base &sock)
112 {
113 SockOpt::Creds creds;
114 if (sock.peercreds(creds))
115 {
116 if (!creds.root_uid())
117 OPENVPN_THROW(ovpnagent, "unix socket server " << host << " not running as root");
118 }
119 else
120 OPENVPN_THROW(ovpnagent, "unix socket server " << host << " could not be validated");
121 };
122
123 return ts;
124 }
125
126 static void make_transaction(const std::string &method,
127 const std::string &content,
128 const bool keepalive,
130 {
131 std::unique_ptr<WS::ClientSet::Transaction> t(new WS::ClientSet::Transaction);
132 t->req.method = "POST";
133 t->req.uri = "/" + method;
134 t->ci.keepalive = keepalive;
135 t->ci.type = "application/json";
136 t->content_out.push_back(buf_from_string(content));
137 ts->transactions.push_back(std::move(t));
138 }
139
140 private:
141 int establish(const TunBuilderCapture &pull, // defined by TunBuilderSetup::Base
142 TunBuilderSetup::Config *tbs_config,
143 Stop *stop,
144 std::ostream &os) override
145 {
146 os << "SetupClient: transmitting tun setup list to " << config->uds_name << std::endl;
147
148 // Build JSON request
149 Json::Value jreq(Json::objectValue);
150 jreq["pid"] = Json::Value(getpid());
151 jreq["tun"] = pull.to_json(); // convert TunBuilderCapture to JSON
152 if (tbs_config)
153 {
154 Json::Value jconf = tbs_config->to_json();
155 if (!jconf.isNull())
156 jreq["config"] = std::move(jconf);
157 }
158 const std::string jtxt = jreq.toStyledString();
159 os << jtxt; // dump it
160
161 // Create HTTP transaction container
163
164 // Set up a completion function to fetch the tunnel fd
165 ScopedFD tun_fd;
166 ts->completion = [&tun_fd](WS::ClientSet::TransactionSet &ts)
167 {
168 if (!ts.http_status_success())
169 return;
170 try
171 {
172 // get HTTP socket
173 const int fd = ts.hsc.unix_fd();
174 if (fd < 0)
175 OPENVPN_THROW_EXCEPTION("cannot get HTTP socket");
176
177 // send FD request
178 XmitFD::xmit_fd(fd, -1, "t", 5000);
179
180 // receive payload FD
181 std::string msg;
182 tun_fd.reset(XmitFD::recv_fd(fd, msg, 256, 5000));
183 if (msg != "T")
184 OPENVPN_THROW_EXCEPTION("bad message tag");
185 }
186 catch (const std::exception &e)
187 {
188 OPENVPN_THROW(ovpnagent, "cannot fetch tunnel fd from agent: " << e.what());
189 }
190 };
191
192 SetupClient::make_transaction("tun-setup", jtxt, true, ts);
193
194 // Execute transaction. sps is true because we need to hold the
195 // HTTP connection state long enough to fetch the received tun socket.
196 WS::ClientSet::new_request_synchronous(ts, stop, nullptr, true);
197
198 // Get result
199 const Json::Value jres = get_json_result(os, *ts);
200
201 // Get config
202 {
203 const Json::Value &jconf = jres["config"];
204 os << jconf.toStyledString();
205 tbs_config->from_json(jconf, "config");
206 }
207
208 // Dump log
209 const std::string log_txt = json::get_string(jres, "log_txt");
210 os << log_txt;
211
212 // return tun fd
213 return tun_fd.release();
214 }
215
216 void destroy(std::ostream &os) override // defined by DestructorBase
217 {
218 os << "SetupClient: transmitting tun destroy request to " << config->uds_name << std::endl;
219
220 // Create HTTP transaction container
222
223 // Make transaction
224 {
225 std::unique_ptr<WS::ClientSet::Transaction> t(new WS::ClientSet::Transaction);
226 t->req.method = "GET";
227 t->req.uri = "/tun-destroy";
228 ts->transactions.push_back(std::move(t));
229 }
230
231 // Execute transaction
233
234 // Process result
235 const Json::Value jres = get_json_result(os, *ts);
236
237 // Dump log
238 const std::string log_txt = json::get_string(jres, "log_txt");
239 os << log_txt;
240 }
241
242 Json::Value get_json_result(std::ostream &os, WS::ClientSet::TransactionSet &ts)
243 {
244 // Get content
245 if (ts.transactions.size() != 1)
246 throw ovpnagent("unexpected transaction set size");
248 const std::string content = t.content_in.to_string();
249 os << t.format_status(ts) << std::endl;
250 if (!t.comm_status_success())
251 {
252 os << content;
253 throw ovpnagent("communication error");
254 }
255 if (!t.request_status_success())
256 {
257 os << content;
258 throw ovpnagent("request error");
259 }
260
261 // Verify content-type
262 if (t.reply.headers.get_value_trim("content-type") != "application/json")
263 {
264 os << content;
265 throw ovpnagent("unexpected content-type");
266 }
267
268 // Parse the returned json dict
269 try
270 {
271 Json::Value jres = json::parse(content);
272 if (!jres.isObject())
273 throw ovpnagent("returned JSON content is not a dictionary");
274 return jres;
275 }
276 catch (const json::json_parse &e)
277 {
278 os << content;
279 OPENVPN_THROW(ovpnagent, "error parsing returned JSON: " << e.what());
280 }
281 }
282
284 };
285
287 {
288 if (config)
289 return new SetupClient(config);
290 else
292 }
293
294 UnixCommandAgent(const OptionList &opt_parent)
295 {
296 config.reset(new Config);
297 }
298
300};
301} // namespace openvpn
302#endif
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
void reset(const int fd_arg)
Definition scoped_fd.hpp:68
Json::Value to_json() const
Serializes the tunnel configuration to a JSON object.
Definition capture.hpp:1011
void destroy(std::ostream &os) override
Definition cmdagent.hpp:216
SetupClient(const Config::Ptr &config_arg)
Definition cmdagent.hpp:92
int establish(const TunBuilderCapture &pull, TunBuilderSetup::Config *tbs_config, Stop *stop, std::ostream &os) override
Definition cmdagent.hpp:141
Json::Value get_json_result(std::ostream &os, WS::ClientSet::TransactionSet &ts)
Definition cmdagent.hpp:242
static void make_transaction(const std::string &method, const std::string &content, const bool keepalive, WS::ClientSet::TransactionSet::Ptr ts)
Definition cmdagent.hpp:126
static WS::ClientSet::TransactionSet::Ptr new_transaction_set(const std::string &host, int debug_level)
Definition cmdagent.hpp:97
static TunBuilderSetup::Factory::Ptr new_agent(const OptionList &opt)
Definition cmdagent.hpp:45
RCPtr< UnixCommandAgent > Ptr
Definition cmdagent.hpp:41
TunBuilderSetup::Base::Ptr new_setup_obj() override
Definition cmdagent.hpp:286
static bool add_bypass_route(IP::Addr endpoint)
Definition cmdagent.hpp:50
UnixCommandAgent(const OptionList &opt_parent)
Definition cmdagent.hpp:294
static void new_request_synchronous(const TransactionSet::Ptr ts, Stop *stop=nullptr, RandomAPI *prng=nullptr, const bool sps=false)
static int recv_fd(const int sock_fd, std::string &message, const size_t buf_size, const int timeout_ms)
Definition xmitfd.hpp:78
static void xmit_fd(const int sock_fd, const int payload_fd, const std::string &message, const int timeout_ms)
Definition xmitfd.hpp:36
#define OPENVPN_THROW_EXCEPTION(stuff)
#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)
Json::Value parse(const std::string &str, 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
bool root_uid() const
Definition peercred.hpp:42
virtual Json::Value to_json()=0
virtual void from_json(const Json::Value &root, const std::string &title)=0
std::string format_status(const TransactionSet &ts) const
proxy_host_port host
std::ostringstream os
#define OVPNAGENT_NAME_STRING
Definition cmdagent.hpp:33
#define msg(flags,...)