1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
// execute a Windows command, capture the output
#ifndef OPENVPN_WIN_CALL_H
#define OPENVPN_WIN_CALL_H
#include <windows.h>
#include <shlobj.h>
#include <knownfolders.h>
#include <cstring>
#include <openvpn/common/uniqueptr.hpp>
#include <openvpn/win/scoped_handle.hpp>
#include <openvpn/win/unicode.hpp>
namespace openvpn::Win {
OPENVPN_EXCEPTION(win_call);
inline std::string call(const std::string &cmd)
{
// split command name from args
std::string name;
std::string args;
const size_t spcidx = cmd.find_first_of(" ");
if (spcidx != std::string::npos)
{
name = cmd.substr(0, spcidx);
if (spcidx + 1 < cmd.length())
args = cmd.substr(spcidx + 1);
}
else
name = cmd;
// get system path
wchar_t *syspath_ptr = nullptr;
if (::SHGetKnownFolderPath(FOLDERID_System, 0, nullptr, &syspath_ptr) != S_OK)
throw win_call("cannot get system path using SHGetKnownFolderPath");
unique_ptr_del<wchar_t> syspath(syspath_ptr,
[](wchar_t *p)
{ ::CoTaskMemFree(p); });
// build command line
const size_t wcmdlen = ::wcslen(syspath.get()) + name.length() + args.length() + 64;
std::unique_ptr<wchar_t[]> wcmd(new wchar_t[wcmdlen]);
const char *spc = "";
if (!args.empty())
spc = " ";
::_snwprintf(wcmd.get(), wcmdlen, L"\"%s\\%S.exe\"%S%S", syspath.get(), name.c_str(), spc, args.c_str());
wcmd.get()[wcmdlen - 1] = 0;
//::wprintf(L"CMD[%d]: %s\n", (int)::wcslen(wcmd.get()), wcmd.get());
// Set the bInheritHandle flag so pipe handles are inherited.
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = nullptr;
// Create a pipe for the child process's STDOUT.
ScopedHANDLE cstdout_r; // child write side
ScopedHANDLE cstdout_w; // parent read side
if (!::CreatePipe(cstdout_r.ref(), cstdout_w.ref(), &saAttr, 0))
throw win_call("cannot create pipe for child stdout");
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!::SetHandleInformation(cstdout_r(), HANDLE_FLAG_INHERIT, 0))
throw win_call("SetHandleInformation failed for child stdout pipe");
// Set up members of the PROCESS_INFORMATION structure.
PROCESS_INFORMATION piProcInfo;
::ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
STARTUPINFOW siStartInfo;
::ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = cstdout_w();
siStartInfo.hStdOutput = cstdout_w();
siStartInfo.hStdInput = nullptr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
if (!::CreateProcessW(nullptr,
wcmd.get(), // command line
nullptr, // process security attributes
nullptr, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
nullptr, // use parent's environment
nullptr, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo)) // receives PROCESS_INFORMATION
throw win_call("cannot create process");
// wrap handles to child process and its primary thread.
ScopedHANDLE process_hand(piProcInfo.hProcess);
ScopedHANDLE thread_hand(piProcInfo.hThread);
// close child's end of stdout/stderr pipe
cstdout_w.close();
// read child's stdout
const size_t outbuf_size = 512;
std::unique_ptr<char[]> outbuf(new char[outbuf_size]);
std::string out;
while (true)
{
DWORD dwRead;
if (!::ReadFile(cstdout_r(), outbuf.get(), outbuf_size, &dwRead, nullptr))
break;
if (dwRead == 0)
break;
out += std::string(outbuf.get(), 0, dwRead);
}
// decode output using console codepage, convert to utf16
// console codepage, used to decode output
UTF16 utf16output(Win::utf16(out, ::GetOEMCP()));
// re-encode utf16 to utf8
UTF8 utf8output(Win::utf8(utf16output.get()));
out.assign(utf8output.get());
// wait for child to exit
if (::WaitForSingleObject(process_hand(), INFINITE) == WAIT_FAILED)
throw win_call("WaitForSingleObject failed on child process handle");
return out;
}
} // namespace openvpn::Win
#endif
|