OpenVPN 3 Core Library
Loading...
Searching...
No Matches
redir.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#ifndef OPENVPN_COMMON_REDIR_H
13#define OPENVPN_COMMON_REDIR_H
14
15#include <fcntl.h>
16#include <unistd.h>
17#include <cstring>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <errno.h>
21
22#include <string>
23#include <utility>
24#include <memory>
25#include <algorithm>
26
27#include <openvpn/io/io.hpp>
28
34
35namespace openvpn {
36
38{
39 OPENVPN_EXCEPTION(redirect_std_err);
40 virtual void redirect() = 0;
41 virtual void close() = 0;
42 virtual ~RedirectBase() = default;
43};
44
46{
47 void redirect() noexcept override
48 {
49 // stdin
50 if (in.defined())
51 {
52 ::dup2(in(), 0);
53 if (in() <= 2)
54 in.release();
55 }
56
57 // stdout
58 if (out.defined())
59 {
60 ::dup2(out(), 1);
61 if (!err.defined() && combine_out_err)
62 ::dup2(out(), 2);
63 if (out() <= 2)
64 out.release();
65 }
66
67 // stderr
68 if (err.defined())
69 {
70 ::dup2(err(), 2);
71 if (err() <= 2)
72 err.release();
73 }
74
75 close();
76 }
77
78 void close() override
79 {
80 in.close();
81 out.close();
82 err.close();
83 }
84
88 bool combine_out_err = false;
89};
90
92{
93 public:
95 {
96 // open /dev/null for stdin
97 in.reset(::open("/dev/null", O_RDONLY, 0));
98 if (!in.defined())
99 {
100 const int eno = errno;
101 OPENVPN_THROW(redirect_std_err, "RedirectNull: error opening /dev/null for input : " << strerror_str(eno));
102 }
103
104 // open /dev/null for stdout
105 out.reset(::open("/dev/null", O_RDWR, 0));
106 if (!out.defined())
107 {
108 const int eno = errno;
109 OPENVPN_THROW(redirect_std_err, "RedirectNull: error opening /dev/null for output : " << strerror_str(eno));
110 }
111 combine_out_err = true;
112 }
113};
114
116{
117 public:
118 // flags shortcuts
119 static constexpr int FLAGS_OVERWRITE = O_CREAT | O_WRONLY | O_TRUNC;
120 static constexpr int FLAGS_APPEND = O_CREAT | O_WRONLY | O_APPEND;
121 static constexpr int FLAGS_MUST_NOT_EXIST = O_CREAT | O_WRONLY | O_EXCL;
122
123 // mode shortcuts
124 static constexpr mode_t MODE_ALL = 0777;
125 static constexpr mode_t MODE_USER_GROUP = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
126 static constexpr mode_t MODE_USER = S_IRUSR | S_IWUSR;
127
128 RedirectStd(const std::string &in_fn,
129 const std::string &out_fn,
130 const int out_flags = FLAGS_OVERWRITE,
131 const mode_t out_mode = MODE_ALL,
132 const bool combine_out_err_arg = true)
133 {
134 if (!in_fn.empty())
135 open_input(in_fn);
136 open_output(out_fn, out_flags, out_mode);
137 combine_out_err = combine_out_err_arg;
138 }
139
140 protected:
142 {
143 }
144
145 void open_input(const std::string &fn)
146 {
147 // open input file for stdin
148 in.reset(::open(fn.c_str(), O_RDONLY, 0));
149 if (!in.defined())
150 {
151 const int eno = errno;
152 OPENVPN_THROW(redirect_std_err, "error opening input file: " << fn << " : " << strerror_str(eno));
153 }
154 }
155
156 void open_output(const std::string &fn,
157 const int flags,
158 const mode_t mode)
159 {
160 // open output file for stdout/stderr
161 out.reset(::open(fn.c_str(),
162 flags,
163 mode));
164 if (!out.defined())
165 {
166 const int eno = errno;
167 OPENVPN_THROW(redirect_std_err, "error opening output file: " << fn << " : " << strerror_str(eno));
168 }
169 }
170};
171
173{
174 public:
175 RedirectTemp(const std::string &stdin_fn,
176 TempFile &stdout_temp,
177 const bool combine_out_err_arg)
178 {
179 open_input(stdin_fn);
180 out = std::move(stdout_temp.fd);
181 combine_out_err = combine_out_err_arg;
182 }
183
184 RedirectTemp(const std::string &stdin_fn,
185 TempFile &stdout_temp,
186 TempFile &stderr_temp)
187 {
188 open_input(stdin_fn);
189 out = std::move(stdout_temp.fd);
190 err = std::move(stderr_temp.fd);
191 }
192};
193
195{
196 public:
197 enum
198 {
199 COMBINE_OUT_ERR = (1 << 0), // capture combined stdout/stderr using a pipe
200 ENABLE_IN = (1 << 1), // make a string -> stdin pipe, otherwise redirect stdin from /dev/null
201 IGNORE_IN = (1 << 2), // don't touch stdin
202 IGNORE_OUT = (1 << 3), // don't touch stdout
203 IGNORE_ERR = (1 << 4), // don't touch stderr
204 };
205
206 struct InOut
207 {
208 std::string in;
209 std::string out;
210 std::string err;
211 };
212
214 {
215 }
216
218 const unsigned int flags_arg)
219 : flags(flags_arg)
220 {
221 // stdout
222 if (!(flags & IGNORE_OUT))
223 {
224 int fd[2];
225 Pipe::make_pipe(fd);
226 out.reset(cloexec(fd[0]));
227 remote.out.reset(fd[1]);
228 }
229
230 // stderr
231 if (!(flags & IGNORE_ERR))
232 {
234 if (!combine_out_err)
235 {
236 int fd[2];
237 Pipe::make_pipe(fd);
238 err.reset(cloexec(fd[0]));
239 remote.err.reset(fd[1]);
240 }
241 }
242
243 // stdin
244 if (!(flags & IGNORE_IN))
245 {
246 if (flags & ENABLE_IN)
247 {
248 int fd[2];
249 Pipe::make_pipe(fd);
250 in.reset(cloexec(fd[1]));
251 remote.in.reset(fd[0]);
252 }
253 else
254 {
255 // open /dev/null for stdin
256 remote.in.reset(::open("/dev/null", O_RDONLY, 0));
257 if (!remote.in.defined())
258 {
259 const int eno = errno;
260 OPENVPN_THROW(redirect_std_err, "error opening /dev/null : " << strerror_str(eno));
261 }
262 }
263 }
264 }
265
266 void transact(InOut &inout)
267 {
268 openvpn_io::io_context io_context(1);
269
270 std::unique_ptr<Pipe::SD_OUT> send_in;
271 std::unique_ptr<Pipe::SD_IN> recv_out;
272 std::unique_ptr<Pipe::SD_IN> recv_err;
273
274 if (!(flags & IGNORE_IN))
275 send_in.reset(new Pipe::SD_OUT(io_context, inout.in, in));
276 if (!(flags & IGNORE_OUT))
277 recv_out.reset(new Pipe::SD_IN(io_context, out));
278 if (!(flags & IGNORE_ERR))
279 recv_err.reset(new Pipe::SD_IN(io_context, err));
280
281 io_context.run();
282
283 if (recv_out)
284 inout.out = recv_out->content();
285 if (recv_err)
286 inout.err = recv_err->content();
287 }
288
289 private:
290 // set FD_CLOEXEC to prevent fd from being passed across execs
291 static int cloexec(const int fd)
292 {
293 if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
294 {
295 const int eno = errno;
296 OPENVPN_THROW(redirect_std_err, "error setting FD_CLOEXEC on pipe : " << strerror_str(eno));
297 }
298 return fd;
299 }
300
301 const unsigned int flags = 0;
302};
303} // namespace openvpn
304
305#endif
void transact(InOut &inout)
Definition redir.hpp:266
RedirectPipe(RedirectStdFD &remote, const unsigned int flags_arg)
Definition redir.hpp:217
static int cloexec(const int fd)
Definition redir.hpp:291
const unsigned int flags
Definition redir.hpp:301
void open_output(const std::string &fn, const int flags, const mode_t mode)
Definition redir.hpp:156
static constexpr int FLAGS_OVERWRITE
Definition redir.hpp:119
static constexpr mode_t MODE_USER_GROUP
Definition redir.hpp:125
static constexpr mode_t MODE_ALL
Definition redir.hpp:124
RedirectStd(const std::string &in_fn, const std::string &out_fn, const int out_flags=FLAGS_OVERWRITE, const mode_t out_mode=MODE_ALL, const bool combine_out_err_arg=true)
Definition redir.hpp:128
static constexpr int FLAGS_MUST_NOT_EXIST
Definition redir.hpp:121
void open_input(const std::string &fn)
Definition redir.hpp:145
static constexpr mode_t MODE_USER
Definition redir.hpp:126
static constexpr int FLAGS_APPEND
Definition redir.hpp:120
RedirectTemp(const std::string &stdin_fn, TempFile &stdout_temp, TempFile &stderr_temp)
Definition redir.hpp:184
RedirectTemp(const std::string &stdin_fn, TempFile &stdout_temp, const bool combine_out_err_arg)
Definition redir.hpp:175
void reset(const int fd_arg)
Definition scoped_fd.hpp:68
bool defined() const
Definition scoped_fd.hpp:58
#define OPENVPN_THROW(exc, stuff)
void make_pipe(int fd[2])
Definition pipe.hpp:119
std::string strerror_str(const int errnum)
Definition strerror.hpp:21
virtual void redirect()=0
virtual ~RedirectBase()=default
virtual void close()=0
OPENVPN_EXCEPTION(redirect_std_err)
void close() override
Definition redir.hpp:78
void redirect() noexcept override
Definition redir.hpp:47
reroute_gw flags