summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2018-09-08 22:46:03 +0200
committerOdd Stranne <odd@mullvad.net>2018-09-12 13:20:20 +0200
commitdf4d2b64449cae87ddab43c634b7f35485e919b3 (patch)
treec8245dc17936e3bd776e03589fa61af8f0324814
parent4cea1a683f2abfb4ee6b0549bdcf9db2517bb400 (diff)
downloadmullvadvpn-df4d2b64449cae87ddab43c634b7f35485e919b3.tar.xz
mullvadvpn-df4d2b64449cae87ddab43c634b7f35485e919b3.zip
Include output from 'netsh' if requested operation fails or times out
-rw-r--r--windows/windns/src/windns/netsh.cpp56
-rw-r--r--windows/windns/src/windns/netsh.h23
2 files changed, 75 insertions, 4 deletions
diff --git a/windows/windns/src/windns/netsh.cpp b/windows/windns/src/windns/netsh.cpp
index af65ace2a9..fc46d824c2 100644
--- a/windows/windns/src/windns/netsh.cpp
+++ b/windows/windns/src/windns/netsh.cpp
@@ -1,21 +1,69 @@
#include "stdafx.h"
#include "netsh.h"
#include "libcommon/applicationrunner.h"
+#include "libcommon/string.h"
#include <sstream>
#include <stdexcept>
namespace
{
+std::vector<std::string> BlockToRows(const std::string &textBlock)
+{
+ //
+ // TODO: Formalize and move to libcommon.
+ // There is a recurring need to split a text block into lines, ignoring blank lines.
+ //
+ // Also, changing the encoding back and forth is terribly wasteful.
+ // Should look into replacing all of this with Boost some day.
+ //
+
+ const auto wideTextBlock = common::string::ToWide(textBlock);
+ const auto wideRows = common::string::Tokenize(wideTextBlock, L"\r\n");
+
+ std::vector<std::string> result;
+
+ result.reserve(wideRows.size());
+
+ std::transform(wideRows.begin(), wideRows.end(), std::back_inserter(result), [](const std::wstring &str)
+ {
+ return common::string::ToAnsi(str);
+ });
+
+ return result;
+}
+
+__declspec(noreturn) void ThrowWithDetails(std::string &&error, common::ApplicationRunner &netsh)
+{
+ std::vector<std::string> details { "Failed to capture output from 'netsh'" };
+
+ std::string output;
+
+ static const size_t MAX_CHARS = 2048;
+ static const size_t TIMEOUT_MILLISECONDS = 2000;
+
+ if (netsh.read(output, MAX_CHARS, TIMEOUT_MILLISECONDS))
+ {
+ auto outputRows = BlockToRows(output);
+
+ if (false == outputRows.empty())
+ {
+ details = std::move(outputRows);
+ }
+ }
+
+ throw NetShError(std::move(error), std::move(details));
+}
+
void ValidateShellOut(common::ApplicationRunner &netsh)
{
- static const uint32_t TIMEOUT_TWO_SECONDS = 2000;
+ static const size_t TIMEOUT_MILLISECONDS = 2000;
DWORD returnCode;
- if (false == netsh.join(returnCode, TIMEOUT_TWO_SECONDS))
+ if (false == netsh.join(returnCode, TIMEOUT_MILLISECONDS))
{
- throw std::runtime_error("'netsh' did not complete in a timely manner");
+ ThrowWithDetails("'netsh' did not complete in a timely manner", netsh);
}
if (returnCode != 0)
@@ -24,7 +72,7 @@ void ValidateShellOut(common::ApplicationRunner &netsh)
ss << "'netsh' failed the requested operation. Error: " << returnCode;
- throw std::runtime_error(ss.str());
+ ThrowWithDetails(ss.str(), netsh);
}
}
diff --git a/windows/windns/src/windns/netsh.h b/windows/windns/src/windns/netsh.h
index d299cf6f87..7aa5b800f0 100644
--- a/windows/windns/src/windns/netsh.h
+++ b/windows/windns/src/windns/netsh.h
@@ -2,6 +2,7 @@
#include <string>
#include <cstdint>
+#include <stdexcept>
class NetSh
{
@@ -24,3 +25,25 @@ private:
NetSh();
};
+
+class NetShError : public std::exception
+{
+public:
+
+ NetShError(std::string &&error, std::vector<std::string> &&details)
+ : std::exception(error.c_str())
+ , m_error(std::move(error))
+ , m_details(std::move(details))
+ {
+ }
+
+ const std::vector<std::string> &details()
+ {
+ return m_details;
+ }
+
+private:
+
+ const std::string m_error;
+ const std::vector<std::string> m_details;
+};