summaryrefslogtreecommitdiff
path: root/src/tapctl/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tapctl/main.c')
-rw-r--r--src/tapctl/main.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/src/tapctl/main.c b/src/tapctl/main.c
new file mode 100644
index 0000000..31bb2ec
--- /dev/null
+++ b/src/tapctl/main.c
@@ -0,0 +1,445 @@
+/*
+ * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2008-2013 David Sommerseth <dazo@users.sourceforge.net>
+ * Copyright (C) 2018 Simon Rozman <simon@rozman.si>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#elif defined(_MSC_VER)
+#include <config-msvc.h>
+#endif
+#ifdef HAVE_CONFIG_VERSION_H
+#include <config-version.h>
+#endif
+
+#include "tap.h"
+#include "error.h"
+
+#include <objbase.h>
+#include <setupapi.h>
+#include <stdio.h>
+#include <tchar.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "ole32.lib")
+#pragma comment(lib, "setupapi.lib")
+#endif
+
+
+const TCHAR title_string[] =
+ TEXT(PACKAGE_NAME) TEXT(" ") TEXT(PACKAGE_VERSION)
+ TEXT(" built on ") TEXT(__DATE__)
+;
+
+static const TCHAR usage_message[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl <command> [<command specific options>]\n")
+ TEXT("\n")
+ TEXT("Commands:\n")
+ TEXT("\n")
+ TEXT("create Create a new TUN/TAP adapter\n")
+ TEXT("list List TUN/TAP adapters\n")
+ TEXT("delete Delete specified network adapter\n")
+ TEXT("help Display this text\n")
+ TEXT("\n")
+ TEXT("Hint: Use \"tapctl help <command>\" to display help for particular command.\n")
+;
+
+static const TCHAR usage_message_create[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Creates a new TUN/TAP adapter\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl create [<options>]\n")
+ TEXT("\n")
+ TEXT("Options:\n")
+ TEXT("\n")
+ TEXT("--name <name> Set TUN/TAP adapter name. Should the adapter with given name \n")
+ TEXT(" already exist, an error is returned. If this option is not \n")
+ TEXT(" specified, a default adapter name is chosen by Windows. \n")
+ TEXT(" Note: This name can also be specified as OpenVPN's --dev-node \n")
+ TEXT(" option. \n")
+ TEXT("--hwid <hwid> Adapter hardware ID. Default value is root\\tap0901, which \n")
+ TEXT(" describes tap-windows6 driver. To work with wintun driver, \n")
+ TEXT(" specify 'wintun'. \n")
+ TEXT("\n")
+ TEXT("Output:\n")
+ TEXT("\n")
+ TEXT("This command prints newly created TUN/TAP adapter's GUID to stdout. \n")
+;
+
+static const TCHAR usage_message_list[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Lists TUN/TAP adapters\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl list\n")
+ TEXT("\n")
+ TEXT("Options:\n")
+ TEXT("\n")
+ TEXT("--hwid <hwid> Adapter hardware ID. By default, root\\tap0901, tap0901 and \n")
+ TEXT(" wintun adapters are listed. Use this switch to limit the list. \n")
+ TEXT("\n")
+ TEXT("Output:\n")
+ TEXT("\n")
+ TEXT("This command prints all TUN/TAP adapters to stdout. \n")
+;
+
+static const TCHAR usage_message_delete[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Deletes the specified network adapter\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl delete <adapter GUID | adapter name>\n")
+;
+
+
+/**
+ * Print the help message.
+ */
+static void
+usage(void)
+{
+ _ftprintf(stderr,
+ usage_message,
+ title_string);
+}
+
+
+/**
+ * Program entry point
+ */
+int __cdecl
+_tmain(int argc, LPCTSTR argv[])
+{
+ int iResult;
+ BOOL bRebootRequired = FALSE;
+
+ /* Ask SetupAPI to keep quiet. */
+ SetupSetNonInteractiveMode(TRUE);
+
+ if (argc < 2)
+ {
+ usage();
+ return 1;
+ }
+ else if (_tcsicmp(argv[1], TEXT("help")) == 0)
+ {
+ /* Output help. */
+ if (argc < 3)
+ {
+ usage();
+ }
+ else if (_tcsicmp(argv[2], TEXT("create")) == 0)
+ {
+ _ftprintf(stderr, usage_message_create, title_string);
+ }
+ else if (_tcsicmp(argv[2], TEXT("list")) == 0)
+ {
+ _ftprintf(stderr, usage_message_list, title_string);
+ }
+ else if (_tcsicmp(argv[2], TEXT("delete")) == 0)
+ {
+ _ftprintf(stderr, usage_message_delete, title_string);
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[2]);
+ }
+
+ return 1;
+ }
+ else if (_tcsicmp(argv[1], TEXT("create")) == 0)
+ {
+ LPCTSTR szName = NULL;
+ LPCTSTR szHwId = TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID);
+
+ /* Parse options. */
+ for (int i = 2; i < argc; i++)
+ {
+ if (_tcsicmp(argv[i], TEXT("--name")) == 0)
+ {
+ szName = argv[++i];
+ }
+ else
+ if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
+ {
+ szHwId = argv[++i];
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown option \"%s\". Please, use \"tapctl help create\" to list supported options. Ignored.\n"), argv[i]);
+ }
+ }
+
+ /* Create TUN/TAP adapter. */
+ GUID guidAdapter;
+ LPOLESTR szAdapterId = NULL;
+ DWORD dwResult = tap_create_adapter(
+ NULL,
+ TEXT("Virtual Ethernet"),
+ szHwId,
+ &bRebootRequired,
+ &guidAdapter);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Creating TUN/TAP adapter failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto quit;
+ }
+
+ if (szName)
+ {
+ /* Get existing network adapters. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Enumerating adapters failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto create_delete_adapter;
+ }
+
+ /* Check for duplicates. */
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
+ {
+ if (_tcsicmp(szName, pAdapter->szName) == 0)
+ {
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+ _ftprintf(stderr, TEXT("Adapter \"%s\" already exists (GUID %") TEXT(PRIsLPOLESTR) TEXT(").\n"), pAdapter->szName, szAdapterId);
+ CoTaskMemFree(szAdapterId);
+ iResult = 1; goto create_cleanup_pAdapterList;
+ }
+ }
+
+ /* Rename the adapter. */
+ dwResult = tap_set_adapter_name(&guidAdapter, szName);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ _ftprintf(stderr, TEXT("Renaming TUN/TAP adapter %") TEXT(PRIsLPOLESTR) TEXT(" to \"%s\" failed (error 0x%x).\n"), szAdapterId, szName, dwResult);
+ CoTaskMemFree(szAdapterId);
+ iResult = 1; goto quit;
+ }
+
+ iResult = 0;
+
+create_cleanup_pAdapterList:
+ tap_free_adapter_list(pAdapterList);
+ if (iResult)
+ {
+ goto create_delete_adapter;
+ }
+ }
+
+ /* Output adapter GUID. */
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\n"), szAdapterId);
+ CoTaskMemFree(szAdapterId);
+
+ iResult = 0; goto quit;
+
+create_delete_adapter:
+ tap_delete_adapter(
+ NULL,
+ &guidAdapter,
+ &bRebootRequired);
+ iResult = 1; goto quit;
+ }
+ else if (_tcsicmp(argv[1], TEXT("list")) == 0)
+ {
+ TCHAR szzHwId[0x100] =
+ TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
+ TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
+ TEXT("Wintun\0");
+
+ /* Parse options. */
+ for (int i = 2; i < argc; i++)
+ {
+ if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
+ {
+ memset(szzHwId, 0, sizeof(szzHwId));
+ ++i;
+ memcpy_s(szzHwId, sizeof(szzHwId) - 2*sizeof(TCHAR) /*requires double zero termination*/, argv[i], _tcslen(argv[i])*sizeof(TCHAR));
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown option \"%s\". Please, use \"tapctl help list\" to list supported options. Ignored.\n"), argv[i]);
+ }
+ }
+
+ /* Output list of adapters with given hardware ID. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto quit;
+ }
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
+ {
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+ _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\t%") TEXT(PRIsLPTSTR) TEXT("\n"), szAdapterId, pAdapter->szName);
+ CoTaskMemFree(szAdapterId);
+ }
+
+ iResult = 0;
+ tap_free_adapter_list(pAdapterList);
+ }
+ else if (_tcsicmp(argv[1], TEXT("delete")) == 0)
+ {
+ if (argc < 3)
+ {
+ _ftprintf(stderr, TEXT("Missing adapter GUID or name. Please, use \"tapctl help delete\" for usage info.\n"));
+ return 1;
+ }
+
+ GUID guidAdapter;
+ if (FAILED(IIDFromString(argv[2], (LPIID)&guidAdapter)))
+ {
+ /* The argument failed to covert to GUID. Treat it as the adapter name. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto quit;
+ }
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList;; pAdapter = pAdapter->pNext)
+ {
+ if (pAdapter == NULL)
+ {
+ _ftprintf(stderr, TEXT("\"%s\" adapter not found.\n"), argv[2]);
+ iResult = 1; goto delete_cleanup_pAdapterList;
+ }
+ else if (_tcsicmp(argv[2], pAdapter->szName) == 0)
+ {
+ memcpy(&guidAdapter, &pAdapter->guid, sizeof(GUID));
+ break;
+ }
+ }
+
+ iResult = 0;
+
+delete_cleanup_pAdapterList:
+ tap_free_adapter_list(pAdapterList);
+ if (iResult)
+ {
+ goto quit;
+ }
+ }
+
+ /* Delete the network adapter. */
+ DWORD dwResult = tap_delete_adapter(
+ NULL,
+ &guidAdapter,
+ &bRebootRequired);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Deleting adapter \"%s\" failed (error 0x%x).\n"), argv[2], dwResult);
+ iResult = 1; goto quit;
+ }
+
+ iResult = 0; goto quit;
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[1]);
+ return 1;
+ }
+
+quit:
+ if (bRebootRequired)
+ {
+ _ftprintf(stderr, TEXT("A system reboot is required.\n"));
+ }
+
+ return iResult;
+}
+
+
+bool
+dont_mute(unsigned int flags)
+{
+ UNREFERENCED_PARAMETER(flags);
+
+ return true;
+}
+
+
+void
+x_msg_va(const unsigned int flags, const char *format, va_list arglist)
+{
+ /* Output message string. Note: Message strings don't contain line terminators. */
+ vfprintf(stderr, format, arglist);
+ _ftprintf(stderr, TEXT("\n"));
+
+ if ((flags & M_ERRNO) != 0)
+ {
+ /* Output system error message (if possible). */
+ DWORD dwResult = GetLastError();
+ LPTSTR szErrMessage = NULL;
+ if (FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ dwResult,
+ 0,
+ (LPTSTR)&szErrMessage,
+ 0,
+ NULL) && szErrMessage)
+ {
+ /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
+ for (size_t i = 0, i_last = 0;; i++)
+ {
+ if (szErrMessage[i])
+ {
+ if (!_istspace(szErrMessage[i]))
+ {
+ i_last = i + 1;
+ }
+ }
+ else
+ {
+ szErrMessage[i_last] = 0;
+ break;
+ }
+ }
+
+ /* Output error message. */
+ _ftprintf(stderr, TEXT("Error 0x%x: %s\n"), dwResult, szErrMessage);
+
+ LocalFree(szErrMessage);
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Error 0x%x\n"), dwResult);
+ }
+ }
+}