0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 12:12:15 +02:00
openvpn3/openvpn/win/call.hpp
2015-05-17 02:53:37 -06:00

156 lines
5.5 KiB
C++

// 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-2015 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// execute a Windows command, capture the output
#ifndef OPENVPN_WIN_CALL_H
#define OPENVPN_WIN_CALL_H
#include <windows.h>
#include <shlobj.h>
#include <openvpn/common/uniqueptr.hpp>
#include <openvpn/win/scoped_handle.hpp>
namespace openvpn {
namespace 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;
#if _WIN32_WINNT >= 0x0600
// get system path (Vista and higher)
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); });
# define SYSPATH_FMT_CHAR L"s"
# define SYSPATH_LEN_METH(x) wcslen(x)
#else
// get system path (XP and higher)
std::unique_ptr<TCHAR[]> syspath(new char[MAX_PATH]);
if (SHGetFolderPath(nullptr, CSIDL_SYSTEM, nullptr, 0, syspath()) != S_OK)
throw win_call("cannot get system path using SHGetFolderPath");
# define SYSPATH_FMT_CHAR L"S"
# define SYSPATH_LEN_METH(x) strlen(x)
#endif
// build command line
const size_t wcmdlen = SYSPATH_LEN_METH(syspath()) + name.length() + args.length() + 64;
std::unique_ptr<wchar_t[]> wcmd(new wchar_t[wcmdlen]);
const char *spc = "";
if (!args.empty())
spc = " ";
_snwprintf(wcmd(), wcmdlen, L"\"%" SYSPATH_FMT_CHAR L"\\%S.exe\"%S%S", syspath(), name.c_str(), spc, args.c_str());
wcmd()[wcmdlen-1] = 0;
//wprintf(L"CMD[%d]: %s\n", (int)wcslen(wcmd()), wcmd());
# undef SYSPATH_FMT_CHAR
# undef SYSPATH_LEN_METH
// 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(), // 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(), outbuf_size, &dwRead, nullptr))
break;
if (dwRead == 0)
break;
out += std::string(outbuf(), 0, dwRead);
}
// wait for child to exit
if (WaitForSingleObject(process_hand(), INFINITE) == WAIT_FAILED)
throw win_call("WaitForSingleObject failed on child process handle");
return out;
}
}
}
#endif