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:
141 RedirectStd() = default;
142
143 void open_input(const std::string &fn)
144 {
145 // open input file for stdin
146 in.reset(::open(fn.c_str(), O_RDONLY, 0));
147 if (!in.defined())
148 {
149 const int eno = errno;
150 OPENVPN_THROW(redirect_std_err, "error opening input file: " << fn << " : " << strerror_str(eno));
151 }
152 }
153
154 void open_output(const std::string &fn,
155 const int flags,
156 const mode_t mode)
157 {
158 // open output file for stdout/stderr
159 out.reset(::open(fn.c_str(),
160 flags,
161 mode));
162 if (!out.defined())
163 {
164 const int eno = errno;
165 OPENVPN_THROW(redirect_std_err, "error opening output file: " << fn << " : " << strerror_str(eno));
166 }
167 }
168};
169
171{
172 public:
173 RedirectTemp(const std::string &stdin_fn,
174 TempFile &stdout_temp,
175 const bool combine_out_err_arg)
176 {
177 open_input(stdin_fn);
178 out = std::move(stdout_temp.fd);
179 combine_out_err = combine_out_err_arg;
180 }
181
182 RedirectTemp(const std::string &stdin_fn,
183 TempFile &stdout_temp,
184 TempFile &stderr_temp)
185 {
186 open_input(stdin_fn);
187 out = std::move(stdout_temp.fd);
188 err = std::move(stderr_temp.fd);
189 }
190};
191
193{
194 public:
195 enum
196 {
197 COMBINE_OUT_ERR = (1 << 0), // capture combined stdout/stderr using a pipe
198 ENABLE_IN = (1 << 1), // make a string -> stdin pipe, otherwise redirect stdin from /dev/null
199 IGNORE_IN = (1 << 2), // don't touch stdin
200 IGNORE_OUT = (1 << 3), // don't touch stdout
201 IGNORE_ERR = (1 << 4), // don't touch stderr
202 };
203
204 struct InOut
205 {
206 std::string in;
207 std::string out;
208 std::string err;
209 };
210
211 RedirectPipe() = default;
212
214 const unsigned int flags_arg)
215 : flags(flags_arg)
216 {
217 // stdout
218 if (!(flags & IGNORE_OUT))
219 {
220 int fd[2];
221 Pipe::make_pipe(fd);
222 out.reset(cloexec(fd[0]));
223 remote.out.reset(fd[1]);
224 }
225
226 // stderr
227 if (!(flags & IGNORE_ERR))
228 {
230 if (!combine_out_err)
231 {
232 int fd[2];
233 Pipe::make_pipe(fd);
234 err.reset(cloexec(fd[0]));
235 remote.err.reset(fd[1]);
236 }
237 }
238
239 // stdin
240 if (!(flags & IGNORE_IN))
241 {
242 if (flags & ENABLE_IN)
243 {
244 int fd[2];
245 Pipe::make_pipe(fd);
246 in.reset(cloexec(fd[1]));
247 remote.in.reset(fd[0]);
248 }
249 else
250 {
251 // open /dev/null for stdin
252 remote.in.reset(::open("/dev/null", O_RDONLY, 0));
253 if (!remote.in.defined())
254 {
255 const int eno = errno;
256 OPENVPN_THROW(redirect_std_err, "error opening /dev/null : " << strerror_str(eno));
257 }
258 }
259 }
260 }
261
262 void transact(InOut &inout)
263 {
264 openvpn_io::io_context io_context(1);
265
266 std::unique_ptr<Pipe::SD_OUT> send_in;
267 std::unique_ptr<Pipe::SD_IN> recv_out;
268 std::unique_ptr<Pipe::SD_IN> recv_err;
269
270 if (!(flags & IGNORE_IN))
271 send_in.reset(new Pipe::SD_OUT(io_context, inout.in, in));
272 if (!(flags & IGNORE_OUT))
273 recv_out.reset(new Pipe::SD_IN(io_context, out));
274 if (!(flags & IGNORE_ERR))
275 recv_err.reset(new Pipe::SD_IN(io_context, err));
276
277 io_context.run();
278
279 if (recv_out)
280 inout.out = recv_out->content();
281 if (recv_err)
282 inout.err = recv_err->content();
283 }
284
285 private:
286 // set FD_CLOEXEC to prevent fd from being passed across execs
287 static int cloexec(const int fd)
288 {
289 if (::fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
290 {
291 const int eno = errno;
292 OPENVPN_THROW(redirect_std_err, "error setting FD_CLOEXEC on pipe : " << strerror_str(eno));
293 }
294 return fd;
295 }
296
297 const unsigned int flags = 0;
298};
299} // namespace openvpn
300
301#endif
void transact(InOut &inout)
Definition redir.hpp:262
RedirectPipe(RedirectStdFD &remote, const unsigned int flags_arg)
Definition redir.hpp:213
static int cloexec(const int fd)
Definition redir.hpp:287
const unsigned int flags
Definition redir.hpp:297
void open_output(const std::string &fn, const int flags, const mode_t mode)
Definition redir.hpp:154
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:143
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:182
RedirectTemp(const std::string &stdin_fn, TempFile &stdout_temp, const bool combine_out_err_arg)
Definition redir.hpp:173
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