Attention is currently required from: flichtenheld, plaisthos.
Hello flichtenheld, plaisthos,
I'd like you to reexamine a change. Please visit
http://gerrit.openvpn.net/c/openvpn/+/1335?usp=email
to look at the new patch set (#3).
Change subject: tapctl: overhaul adapter management helpers and improve CLI UX
......................................................................
tapctl: overhaul adapter management helpers and improve CLI UX
- default to creating ovpn-dco adapters, refresh help text,
validate CLI input, and print both GUID and name after creation;
- refactor create/list/delete into dedicated helpers,
add bulk deletion via delete --hwid, and centralize
GUID/name resolution;
- harden auto-naming to recognise both ovpn-dco and
tap0901 HWIDs, honour reserved names, and reuse friendly names
when deleting by GUID.
- add name validation according to Windows connection-name rules.
Change-Id: I4d822bf7ae5f037e2414778e8fa74eca5e289e65
Signed-off-by: Lev Stipakov <[email protected]>
---
M src/tapctl/main.c
M src/tapctl/tap.c
M src/tapctl/tap.h
3 files changed, 658 insertions(+), 243 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/35/1335/3
diff --git a/src/tapctl/main.c b/src/tapctl/main.c
index 031e262..649c399 100644
--- a/src/tapctl/main.c
+++ b/src/tapctl/main.c
@@ -1,5 +1,5 @@
/*
- * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
+ * tapctl -- Utility to manipulate virtual network adapters on Windows
* https://community.openvpn.net/openvpn/wiki/Tapctl
*
* Copyright (C) 2002-2025 OpenVPN Inc <[email protected]>
@@ -28,6 +28,7 @@
#include <objbase.h>
#include <setupapi.h>
#include <stdio.h>
+#include <stdlib.h>
#include <wchar.h>
#ifdef _MSC_VER
@@ -50,8 +51,8 @@
L"\n"
L"Commands:\n"
L"\n"
- L"create Create a new TUN/TAP adapter\n"
- L"list List TUN/TAP adapters\n"
+ L"create Create a new network adapter\n"
+ L"list List network adapters\n"
L"delete Delete specified network adapter\n"
L"help Display this text\n"
L"\n"
@@ -61,7 +62,7 @@
static const WCHAR usage_message_create[] =
L"%ls\n"
L"\n"
- L"Creates a new TUN/TAP adapter\n"
+ L"Creates a new network adapter\n"
L"\n"
L"Usage:\n"
L"\n"
@@ -69,24 +70,24 @@
L"\n"
L"Options:\n"
L"\n"
- L"--name <name> Set TUN/TAP adapter name. Should the adapter with given
name \n"
+ L"--name <name> Set network adapter name. Should the adapter with given
name \n"
L" already exist, an error is returned. If this option is
not \n"
L" specified, a default adapter name is chosen by Windows.
\n"
L" Note: This name can also be specified as OpenVPN's
--dev-node \n"
L" option.
\n"
- L"--hwid <hwid> Adapter hardware ID. Default value is root\\tap0901,
which \n"
- L" describes tap-windows6 driver. To work with ovpn-dco
driver, \n"
- L" driver, specify 'ovpn-dco'.
\n"
+ L"--hwid <hwid> Adapter hardware ID. Default value is ovpn-dco, which
creates \n"
+ L" a Data Channel Offload adapter. To create a tap-windows6
\n"
+ L" adapter, specify 'root\\tap0901'.
\n"
L"\n"
L"Output:\n"
L"\n"
- L"This command prints newly created TUN/TAP adapter's GUID to stdout.
\n"
+ L"This command prints newly created network adapter's GUID to stdout.
\n"
;
static const WCHAR usage_message_list[] =
L"%ls\n"
L"\n"
- L"Lists TUN/TAP adapters\n"
+ L"Lists network adapters\n"
L"\n"
L"Usage:\n"
L"\n"
@@ -94,22 +95,28 @@
L"\n"
L"Options:\n"
L"\n"
- L"--hwid <hwid> Adapter hardware ID. By default, root\\tap0901, tap0901
and \n"
- L" ovpn-dco adapters are listed. Use this switch to limit
the list.\n"
+ L"--hwid <hwid> Adapter hardware ID. By default, ovpn-dco, root\\tap0901
and \n"
+ L" tap0901 adapters are listed. Use this switch to limit the
list.\n"
L"\n"
L"Output:\n"
L"\n"
- L"This command prints all TUN/TAP adapters to stdout.
\n"
+ L"This command prints all network adapters to stdout.
\n"
;
static const WCHAR usage_message_delete[] =
L"%ls\n"
L"\n"
- L"Deletes the specified network adapter\n"
+ L"Deletes network adapters\n"
L"\n"
L"Usage:\n"
L"\n"
L"tapctl delete <adapter GUID | adapter name>\n"
+ L"tapctl delete --hwid <hardware ID>\n"
+ L"\n"
+ L"Options:\n"
+ L"\n"
+ L"--hwid <hwid> Delete all adapters that use the specified hardware ID.
Cannot\n"
+ L" be combined with adapter GUID or name.\n"
;
/* clang-format on */
@@ -123,84 +130,642 @@
fwprintf(stderr, usage_message, title_string);
}
+static int tapctl_cmd_create(int argc, LPCWSTR argv[], BOOL *pbRebootRequired);
+static int tapctl_cmd_list(int argc, LPCWSTR argv[]);
+static int tapctl_cmd_delete(int argc, LPCWSTR argv[], BOOL *pbRebootRequired);
+
/**
- * Checks if adapter with given name doesn't already exist
+ * Locate an adapter node by its friendly name within the enumerated list.
+ *
+ * @param name Friendly name to search for. Comparison is
case-insensitive.
+ * @param adapter_list Head of the adapter list returned by
tap_list_adapters().
+ *
+ * @return Pointer to the matching node, or NULL when not found.
*/
-static BOOL
-is_adapter_name_available(LPCWSTR name, struct tap_adapter_node *adapter_list,
BOOL log)
+static struct tap_adapter_node *
+find_adapter_by_name(LPCWSTR name, struct tap_adapter_node *adapter_list)
{
for (struct tap_adapter_node *a = adapter_list; a; a = a->pNext)
{
- if (wcsicmp(name, a->szName) == 0)
+ if (_wcsicmp(name, a->szName) == 0)
{
- if (log)
- {
- LPOLESTR adapter_id = NULL;
- StringFromIID((REFIID)&a->guid, &adapter_id);
- fwprintf(stderr,
- L"Adapter \"%ls\" already exists (GUID %"
- L"ls).\n",
- a->szName, adapter_id);
- CoTaskMemFree(adapter_id);
- }
-
- return FALSE;
+ return a;
}
}
- return TRUE;
+ return NULL;
}
/**
- * Returns unique adapter name based on hwid or NULL if name cannot be
generated.
- * Caller is responsible for freeing it.
+ * Check whether the registry still reserves a given network-connection name.
+ *
+ * Windows keeps friendly names under
+ *
\\HKLM\\SYSTEM\\CurrentControlSet\\Control\\Network\\{NETCLASS}\\{GUID}\\Connection\\Name,
+ * even after an adapter is removed. netsh refuses to rename to any reserved
name.
+ *
+ * @param name Friendly name to test.
+ *
+ * @return TRUE if the name exists in the registry, FALSE otherwise.
+ */
+static BOOL
+registry_name_exists(LPCWSTR name)
+{
+ static const WCHAR class_key[] =
+
L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4d36e972-e325-11ce-bfc1-08002be10318}";
+
+ HKEY hClassKey = NULL;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, class_key, 0, KEY_READ, &hClassKey)
!= ERROR_SUCCESS)
+ {
+ return FALSE;
+ }
+
+ BOOL found = FALSE;
+
+ for (DWORD index = 0;; ++index)
+ {
+ WCHAR adapter_id[64];
+ DWORD adapter_id_len = _countof(adapter_id);
+ LONG result = RegEnumKeyEx(hClassKey, index, adapter_id,
&adapter_id_len, NULL, NULL, NULL,
+ NULL);
+ if (result == ERROR_NO_MORE_ITEMS)
+ {
+ break;
+ }
+ else if (result != ERROR_SUCCESS)
+ {
+ continue;
+ }
+
+ WCHAR connection_key[512];
+ swprintf_s(connection_key, _countof(connection_key),
L"%ls\\%ls\\Connection", class_key,
+ adapter_id);
+
+ DWORD value_size = 0;
+ LONG query = RegGetValueW(HKEY_LOCAL_MACHINE, connection_key, L"Name",
+ RRF_RT_REG_SZ | RRF_NOEXPAND, NULL, NULL,
&value_size);
+ if (query != ERROR_SUCCESS || value_size < sizeof(WCHAR))
+ {
+ continue;
+ }
+
+ LPWSTR value = (LPWSTR)malloc(value_size);
+ if (!value)
+ {
+ continue;
+ }
+
+ query = RegGetValueW(HKEY_LOCAL_MACHINE, connection_key, L"Name",
+ RRF_RT_REG_SZ | RRF_NOEXPAND, NULL, value,
&value_size);
+ if (query == ERROR_SUCCESS && _wcsicmp(name, value) == 0)
+ {
+ found = TRUE;
+ free(value);
+ break;
+ }
+
+ free(value);
+
+ if (found)
+ {
+ break;
+ }
+ }
+
+ RegCloseKey(hClassKey);
+ return found;
+}
+
+/**
+ * Determine whether a friendly name is currently in use by an adapter or
reserved
+ * in the registry.
+ *
+ * @param name Friendly name to test.
+ * @param adapter_list Head of the adapter list returned by
tap_list_adapters().
+ *
+ * @return TRUE when the name is taken/reserved, FALSE when available.
+ */
+static BOOL
+tap_name_in_use(LPCWSTR name, struct tap_adapter_node *adapter_list)
+{
+ if (name == NULL)
+ {
+ return FALSE;
+ }
+
+ if (find_adapter_by_name(name, adapter_list))
+ {
+ return TRUE;
+ }
+
+ return registry_name_exists(name);
+}
+
+/**
+ * Resolve the adapter name we should apply:
+ * - For user-specified names, ensure they are unique both in the adapter
list and
+ * in the registry. On conflict, an explanatory message is printed and
NULL is returned.
+ * - For automatic naming, derive the base string from HWID and append the
first available
+ * suffix recognised by Windows.
+ *
+ * @param requested_name Name provided via CLI or configuration (may be
NULL/empty).
+ * @param hwid Hardware identifier of the adapter being created.
+ * @param adapter_list Existing adapters enumerated via tap_list_adapters().
+ *
+ * @return Newly allocated wide string containing the final name, or NULL on
failure.
*/
static LPWSTR
-get_unique_adapter_name(LPCWSTR hwid, struct tap_adapter_node *adapter_list)
+tap_resolve_adapter_name(LPCWSTR requested_name, LPCWSTR hwid,
+ struct tap_adapter_node *adapter_list)
{
+ if (requested_name && requested_name[0])
+ {
+ if (!tap_is_valid_adapter_name(requested_name))
+ {
+ fwprintf(stderr,
+ L"Adapter name \"%ls\" contains invalid characters. Avoid
tabs or the "
+ L"characters \\ / : * ? \" < > | and keep the length
within 255 characters.\n",
+ requested_name);
+ return NULL;
+ }
+
+ struct tap_adapter_node *conflict =
find_adapter_by_name(requested_name, adapter_list);
+ if (conflict)
+ {
+ LPOLESTR adapter_id = NULL;
+ StringFromIID((REFIID)&conflict->guid, &adapter_id);
+ fwprintf(stderr,
+ L"Adapter \"%ls\" already exists (GUID %"
+ L"ls).\n",
+ conflict->szName, adapter_id);
+ CoTaskMemFree(adapter_id);
+ return NULL;
+ }
+
+ if (registry_name_exists(requested_name))
+ {
+ fwprintf(stderr, L"Adapter name \"%ls\" is already in use.\n",
requested_name);
+ return NULL;
+ }
+
+ return wcsdup(requested_name);
+ }
+
if (hwid == NULL)
{
return NULL;
}
- LPCWSTR base_name;
- if (wcsicmp(hwid, L"ovpn-dco") == 0)
+ LPCWSTR base_name = NULL;
+ if (_wcsicmp(hwid, L"ovpn-dco") == 0)
{
base_name = L"OpenVPN Data Channel Offload";
}
- else if (wcsicmp(hwid, L"root\\" _L(TAP_WIN_COMPONENT_ID)) == 0)
+ else if (_wcsicmp(hwid, L"root\\" _L(TAP_WIN_COMPONENT_ID)) == 0
+ || _wcsicmp(hwid, _L(TAP_WIN_COMPONENT_ID)) == 0)
{
base_name = L"OpenVPN TAP-Windows6";
}
else
{
+ fwprintf(stderr,
+ L"Cannot auto-generate adapter name for hardware ID
\"%ls\".\n", hwid);
return NULL;
}
- if (is_adapter_name_available(base_name, adapter_list, FALSE))
+ if (!tap_name_in_use(base_name, adapter_list))
{
return wcsdup(base_name);
}
size_t name_len = wcslen(base_name) + 10;
- LPWSTR name = malloc(name_len * sizeof(WCHAR));
+ LPWSTR name = (LPWSTR)malloc(name_len * sizeof(WCHAR));
if (name == NULL)
{
return NULL;
}
- for (int i = 1; i < 100; ++i)
+
+ /* Windows never assigns the "#1" suffix, so skip it to avoid netsh
failures. */
+ for (int i = 2; i < 100; ++i)
{
swprintf_s(name, name_len, L"%ls #%d", base_name, i);
- if (is_adapter_name_available(name, adapter_list, FALSE))
+ if (!tap_name_in_use(name, adapter_list))
{
return name;
}
}
+ free(name);
+ fwprintf(stderr, L"Unable to find available adapter name based on
\"%ls\".\n", base_name);
return NULL;
}
+static int
+tapctl_cmd_create(int argc, LPCWSTR argv[], BOOL *pbRebootRequired)
+{
+ LPCWSTR szName = NULL;
+ LPCWSTR defaultHwId = L"ovpn-dco";
+ LPCWSTR szHwId = defaultHwId;
+ LPWSTR adapter_name = NULL;
+ struct tap_adapter_node *pAdapterList = NULL;
+ GUID guidAdapter;
+ LPOLESTR szAdapterId = NULL;
+ DWORD dwResult;
+ int iResult = 1;
+ BOOL adapter_created = FALSE;
+
+ for (int i = 2; i < argc; i++)
+ {
+ if (wcsicmp(argv[i], L"--name") == 0)
+ {
+ if (++i >= argc)
+ {
+ fwprintf(stderr, L"--name option requires a value.
Ignored.\n");
+ break;
+ }
+ szName = argv[i];
+ if (szName[0] == L'\0')
+ {
+ fwprintf(stderr, L"--name option cannot be empty. Ignored.\n");
+ szName = NULL;
+ }
+ }
+ else if (wcsicmp(argv[i], L"--hwid") == 0)
+ {
+ if (++i >= argc)
+ {
+ fwprintf(stderr,
+ L"--hwid option requires a value. Using default
\"%ls\".\n",
+ defaultHwId);
+ break;
+ }
+ szHwId = argv[i];
+ if (szHwId[0] == L'\0')
+ {
+ fwprintf(stderr,
+ L"--hwid option cannot be empty. Using default
\"%ls\".\n",
+ defaultHwId);
+ szHwId = defaultHwId;
+ }
+ }
+ else
+ {
+ fwprintf(stderr,
+ L"Unknown option \"%ls"
+ L"\". Please, use \"tapctl help create\" to list
supported options. Ignored.\n",
+ argv[i]);
+ }
+ }
+
+ dwResult = tap_create_adapter(NULL, L"Virtual Ethernet", szHwId,
pbRebootRequired,
+ &guidAdapter);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr, L"Creating network adapter failed (error 0x%x).\n",
dwResult);
+ goto cleanup;
+ }
+ adapter_created = TRUE;
+
+ dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr, L"Enumerating adapters failed (error 0x%x).\n",
dwResult);
+ goto cleanup;
+ }
+
+ adapter_name = tap_resolve_adapter_name(szName, szHwId, pAdapterList);
+ if (adapter_name == NULL)
+ {
+ goto cleanup;
+ }
+
+ dwResult = tap_set_adapter_name(&guidAdapter, adapter_name, FALSE);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ fwprintf(stderr,
+ L"Renaming network adapter %ls to \"%ls\" failed (error
0x%x).\n", szAdapterId,
+ adapter_name, dwResult);
+ CoTaskMemFree(szAdapterId);
+ goto cleanup;
+ }
+
+ iResult = 0;
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ fwprintf(stdout, L"%ls\t%ls\n", szAdapterId,
+ (adapter_name && adapter_name[0]) ? adapter_name : L"(unnamed)");
+ CoTaskMemFree(szAdapterId);
+
+cleanup:
+ if (pAdapterList)
+ {
+ tap_free_adapter_list(pAdapterList);
+ }
+ free(adapter_name);
+
+ if (adapter_created && iResult != 0)
+ {
+ tap_delete_adapter(NULL, &guidAdapter, pbRebootRequired);
+ }
+
+ return iResult;
+}
+
+static int
+tapctl_cmd_list(int argc, LPCWSTR argv[])
+{
+ WCHAR szzHwId[0x100] = L"ovpn-dco\0"
+ L"root\\" _L(TAP_WIN_COMPONENT_ID) L"\0"
_L(TAP_WIN_COMPONENT_ID) L"\0";
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult;
+ int iResult = 0;
+
+ for (int i = 2; i < argc; i++)
+ {
+ if (wcsicmp(argv[i], L"--hwid") == 0)
+ {
+ if (++i >= argc)
+ {
+ fwprintf(stderr,
+ L"--hwid option requires a value. Please, use
\"tapctl help list\" to "
+ L"list supported options.\n");
+ return 1;
+ }
+ memset(szzHwId, 0, sizeof(szzHwId));
+ memcpy_s(szzHwId,
+ sizeof(szzHwId) - 2 * sizeof(WCHAR) /*requires double
zero termination*/,
+ argv[i], wcslen(argv[i]) * sizeof(WCHAR));
+ }
+ else
+ {
+ fwprintf(stderr,
+ L"Unknown option \"%ls"
+ L"\". Please, use \"tapctl help list\" to list supported
options. Ignored.\n",
+ argv[i]);
+ }
+ }
+
+ dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr, L"Enumerating network adapters failed (error
0x%x).\n", dwResult);
+ return 1;
+ }
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter
= pAdapter->pNext)
+ {
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+ fwprintf(stdout, L"%ls\t%ls\n", szAdapterId, pAdapter->szName);
+ CoTaskMemFree(szAdapterId);
+ }
+
+ tap_free_adapter_list(pAdapterList);
+ return iResult;
+}
+
+/**
+ * Delete every adapter matching the provided hardware ID.
+ *
+ * @param hwid Hardware ID string (must be non-NULL and non-empty).
+ * @param pbRebootRequired Pointer to reboot flag propagated from caller.
+ *
+ * @return 0 when every adapter was deleted successfully, 1 otherwise.
+ */
+static int
+tapctl_delete_by_hwid(LPCWSTR hwid, BOOL *pbRebootRequired)
+{
+ if (hwid == NULL || hwid[0] == L'\0')
+ {
+ fwprintf(stderr, L"--hwid option cannot be empty.\n");
+ return 1;
+ }
+
+ size_t hw_len = wcslen(hwid);
+ size_t buf_len = hw_len + 2;
+ LPWSTR szzHwId = (LPWSTR)calloc(buf_len, sizeof(WCHAR));
+ if (szzHwId == NULL)
+ {
+ fwprintf(stderr, L"Out of memory.\n");
+ return 1;
+ }
+ wmemcpy(szzHwId, hwid, hw_len);
+
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
+ free(szzHwId);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr, L"Enumerating network adapters failed (error
0x%x).\n", dwResult);
+ return 1;
+ }
+
+ BOOL deleted_any = FALSE;
+ BOOL delete_error = FALSE;
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter
= pAdapter->pNext)
+ {
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+
+ dwResult = tap_delete_adapter(NULL, &pAdapter->guid, pbRebootRequired);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr,
+ L"Deleting adapter \"%ls\" (GUID %ls) failed (error
0x%x).\n",
+ pAdapter->szName, szAdapterId, dwResult);
+ delete_error = TRUE;
+ }
+ else
+ {
+ fwprintf(stdout, L"%ls\t%ls\n", szAdapterId, pAdapter->szName);
+ deleted_any = TRUE;
+ }
+
+ CoTaskMemFree(szAdapterId);
+ }
+
+ tap_free_adapter_list(pAdapterList);
+
+ if (!deleted_any && !delete_error)
+ {
+ fwprintf(stderr, L"No adapters with hardware ID \"%ls\" found.\n",
hwid);
+ }
+
+ return delete_error ? 1 : 0;
+}
+
+/**
+ * Resolve an adapter identifier supplied on the command line.
+ *
+ * @param target Adapter GUID or friendly name provided by the user.
+ * @param pguidAdapter Receives the resolved adapter GUID on success.
+ * @param pszMatchedName Optional pointer that receives a duplicated adapter
name when
+ * the lookup was done by name. Caller must free() it.
+ *
+ * @return 0 on success, non-zero otherwise (an error message is printed on
failure).
+ */
+static int
+tapctl_resolve_delete_target(LPCWSTR target, GUID *pguidAdapter, LPWSTR
*pszMatchedName)
+{
+ if (SUCCEEDED(IIDFromString(target, (LPIID)pguidAdapter)))
+ {
+ if (pszMatchedName)
+ {
+ *pszMatchedName = NULL;
+ struct tap_adapter_node *pAdapterList = NULL;
+ if (tap_list_adapters(NULL, NULL, &pAdapterList) == ERROR_SUCCESS)
+ {
+ for (struct tap_adapter_node *pAdapter = pAdapterList;
pAdapter;
+ pAdapter = pAdapter->pNext)
+ {
+ if (IsEqualGUID(&pAdapter->guid, pguidAdapter))
+ {
+ LPWSTR duplicated = wcsdup(pAdapter->szName);
+ if (duplicated == NULL)
+ {
+ fwprintf(stderr, L"Out of memory.\n");
+ tap_free_adapter_list(pAdapterList);
+ return 1;
+ }
+ *pszMatchedName = duplicated;
+ break;
+ }
+ }
+ tap_free_adapter_list(pAdapterList);
+ }
+ }
+ return 0;
+ }
+
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr, L"Enumerating network adapters failed (error
0x%x).\n", dwResult);
+ return 1;
+ }
+
+ struct tap_adapter_node *match = NULL;
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter
= pAdapter->pNext)
+ {
+ if (wcsicmp(target, pAdapter->szName) == 0)
+ {
+ match = pAdapter;
+ break;
+ }
+ }
+
+ if (match == NULL)
+ {
+ fwprintf(stderr, L"\"%ls\" adapter not found.\n", target);
+ tap_free_adapter_list(pAdapterList);
+ return 1;
+ }
+
+ LPWSTR duplicated_name = pszMatchedName ? wcsdup(match->szName) : NULL;
+ if (pszMatchedName && duplicated_name == NULL)
+ {
+ fwprintf(stderr, L"Out of memory.\n");
+ tap_free_adapter_list(pAdapterList);
+ return 1;
+ }
+
+ memcpy(pguidAdapter, &match->guid, sizeof(GUID));
+ tap_free_adapter_list(pAdapterList);
+
+ if (pszMatchedName)
+ {
+ *pszMatchedName = duplicated_name;
+ }
+ return 0;
+}
+
+static int
+tapctl_cmd_delete(int argc, LPCWSTR argv[], BOOL *pbRebootRequired)
+{
+ LPCWSTR szDeleteTarget = NULL;
+ LPCWSTR szDeleteHwId = NULL;
+
+ for (int i = 2; i < argc; i++)
+ {
+ if (wcsicmp(argv[i], L"--hwid") == 0)
+ {
+ if (++i >= argc)
+ {
+ fwprintf(stderr,
+ L"--hwid option requires a value. Please, use
\"tapctl help delete\" for "
+ L"usage info.\n");
+ return 1;
+ }
+ if (argv[i][0] == L'\0')
+ {
+ fwprintf(stderr,
+ L"--hwid option cannot be empty. Please, use \"tapctl
help delete\" for "
+ L"usage info.\n");
+ return 1;
+ }
+ szDeleteHwId = argv[i];
+ }
+ else if (szDeleteTarget == NULL)
+ {
+ szDeleteTarget = argv[i];
+ }
+ else
+ {
+ fwprintf(stderr,
+ L"Unknown option \"%ls"
+ L"\". Please, use \"tapctl help delete\" for usage info.
Ignored.\n",
+ argv[i]);
+ }
+ }
+
+ if (szDeleteHwId)
+ {
+ if (szDeleteTarget)
+ {
+ fwprintf(stderr, L"--hwid option cannot be combined with adapter
GUID or name.\n");
+ return 1;
+ }
+ return tapctl_delete_by_hwid(szDeleteHwId, pbRebootRequired);
+ }
+
+ if (szDeleteTarget == NULL)
+ {
+ fwprintf(
+ stderr,
+ L"Missing adapter GUID or name. Please, use \"tapctl help delete\"
for usage info.\n");
+ return 1;
+ }
+
+ GUID guidAdapter;
+ LPWSTR matched_name = NULL;
+ if (tapctl_resolve_delete_target(szDeleteTarget, &guidAdapter,
&matched_name) != 0)
+ {
+ return 1;
+ }
+
+ DWORD dwResult = tap_delete_adapter(NULL, &guidAdapter, pbRebootRequired);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ fwprintf(stderr,
+ L"Deleting adapter \"%ls"
+ L"\" failed (error 0x%x).\n",
+ szDeleteTarget, dwResult);
+ free(matched_name);
+ return 1;
+ }
+
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ fwprintf(stdout, L"%ls\t%ls\n", szAdapterId,
+ (matched_name && matched_name[0]) ? matched_name :
szDeleteTarget);
+ CoTaskMemFree(szAdapterId);
+ free(matched_name);
+
+ return 0;
+}
+
/**
* Program entry point
*/
@@ -248,215 +813,17 @@
}
else if (wcsicmp(argv[1], L"create") == 0)
{
- LPCWSTR szName = NULL;
- LPCWSTR szHwId = L"root\\" _L(TAP_WIN_COMPONENT_ID);
-
- /* Parse options. */
- for (int i = 2; i < argc; i++)
- {
- if (wcsicmp(argv[i], L"--name") == 0)
- {
- szName = argv[++i];
- }
- else if (wcsicmp(argv[i], L"--hwid") == 0)
- {
- szHwId = argv[++i];
- }
- else
- {
- fwprintf(
- stderr,
- L"Unknown option \"%ls"
- L"\". 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, L"Virtual Ethernet", szHwId,
&bRebootRequired, &guidAdapter);
- if (dwResult != ERROR_SUCCESS)
- {
- fwprintf(stderr, L"Creating TUN/TAP adapter failed (error
0x%x).\n", dwResult);
- iResult = 1;
- goto quit;
- }
-
- /* Get existing network adapters. */
- struct tap_adapter_node *pAdapterList = NULL;
- dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
- if (dwResult != ERROR_SUCCESS)
- {
- fwprintf(stderr, L"Enumerating adapters failed (error 0x%x).\n",
dwResult);
- iResult = 1;
- goto create_delete_adapter;
- }
-
- LPWSTR adapter_name =
- szName ? wcsdup(szName) : get_unique_adapter_name(szHwId,
pAdapterList);
- if (adapter_name)
- {
- /* Check for duplicates when name was specified,
- * otherwise get_adapter_default_name() takes care of it */
- if (szName && !is_adapter_name_available(adapter_name,
pAdapterList, TRUE))
- {
- iResult = 1;
- goto create_cleanup_pAdapterList;
- }
-
- /* Rename the adapter. */
- dwResult = tap_set_adapter_name(&guidAdapter, adapter_name, FALSE);
- if (dwResult != ERROR_SUCCESS)
- {
- StringFromIID((REFIID)&guidAdapter, &szAdapterId);
- fwprintf(stderr,
- L"Renaming TUN/TAP adapter %ls"
- L" to \"%ls\" failed (error 0x%x).\n",
- szAdapterId, adapter_name, dwResult);
- CoTaskMemFree(szAdapterId);
- iResult = 1;
- goto quit;
- }
- }
-
- iResult = 0;
-
-create_cleanup_pAdapterList:
- free(adapter_name);
-
- tap_free_adapter_list(pAdapterList);
- if (iResult)
- {
- goto create_delete_adapter;
- }
-
- /* Output adapter GUID. */
- StringFromIID((REFIID)&guidAdapter, &szAdapterId);
- fwprintf(stdout, L"%ls\n", szAdapterId);
- CoTaskMemFree(szAdapterId);
-
- iResult = 0;
- goto quit;
-
-create_delete_adapter:
- tap_delete_adapter(NULL, &guidAdapter, &bRebootRequired);
- iResult = 1;
+ iResult = tapctl_cmd_create(argc, argv, &bRebootRequired);
goto quit;
}
else if (wcsicmp(argv[1], L"list") == 0)
{
- WCHAR szzHwId[0x100] =
- L"root\\" _L(TAP_WIN_COMPONENT_ID) L"\0" _L(TAP_WIN_COMPONENT_ID)
L"\0"
-
L"ovpn-dco\0";
-
- /* Parse options. */
- for (int i = 2; i < argc; i++)
- {
- if (wcsicmp(argv[i], L"--hwid") == 0)
- {
- memset(szzHwId, 0, sizeof(szzHwId));
- ++i;
- memcpy_s(szzHwId,
- sizeof(szzHwId) - 2 * sizeof(WCHAR) /*requires double
zero termination*/,
- argv[i], wcslen(argv[i]) * sizeof(WCHAR));
- }
- else
- {
- fwprintf(
- stderr,
- L"Unknown option \"%ls"
- L"\". 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)
- {
- fwprintf(stderr, L"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);
- fwprintf(stdout,
- L"%ls\t%"
- L"ls\n",
- szAdapterId, pAdapter->szName);
- CoTaskMemFree(szAdapterId);
- }
-
- iResult = 0;
- tap_free_adapter_list(pAdapterList);
+ iResult = tapctl_cmd_list(argc, argv);
+ goto quit;
}
else if (wcsicmp(argv[1], L"delete") == 0)
{
- if (argc < 3)
- {
- fwprintf(
- stderr,
- L"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)
- {
- fwprintf(stderr, L"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)
- {
- fwprintf(stderr, L"\"%ls\" adapter not found.\n", argv[2]);
- iResult = 1;
- goto delete_cleanup_pAdapterList;
- }
- else if (wcsicmp(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)
- {
- fwprintf(stderr,
- L"Deleting adapter \"%ls"
- L"\" failed (error 0x%x).\n",
- argv[2], dwResult);
- iResult = 1;
- goto quit;
- }
-
- iResult = 0;
+ iResult = tapctl_cmd_delete(argc, argv, &bRebootRequired);
goto quit;
}
else
diff --git a/src/tapctl/tap.c b/src/tapctl/tap.c
index 9731602..bb2dfe3 100644
--- a/src/tapctl/tap.c
+++ b/src/tapctl/tap.c
@@ -921,6 +921,45 @@
bEnable ? enable_device : disable_device,
pbRebootRequired);
}
+/**
+ * Check whether a proposed adapter name satisfies Windows connection-name
rules.
+ *
+ * Tabs, control characters (except space), and the following characters are
disallowed:
+ * \ / : * ? " < > |
+ * Names must also be non-empty and no longer than 255 characters.
+ */
+BOOL
+tap_is_valid_adapter_name(LPCWSTR name)
+{
+ if (name == NULL)
+ {
+ return FALSE;
+ }
+
+ size_t length = wcslen(name);
+ if (length == 0 || length > 255)
+ {
+ return FALSE;
+ }
+
+ static const WCHAR invalid_chars[] = L"\\/:*?\"<>|";
+
+ for (const WCHAR *p = name; *p; ++p)
+ {
+ WCHAR ch = *p;
+ if (ch < L' ')
+ {
+ return FALSE;
+ }
+ if (wcschr(invalid_chars, ch))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
/* stripped version of ExecCommand in interactive.c */
static DWORD
ExecCommand(const WCHAR *cmdline)
@@ -971,6 +1010,13 @@
return ERROR_BAD_ARGUMENTS;
}
+ if (!tap_is_valid_adapter_name(szName))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ msg(msg_flag, "%s: invalid adapter name \"%ls\"", __FUNCTION__,
szName);
+ return ERROR_INVALID_PARAMETER;
+ }
+
/* Get the device class GUID as string. */
LPOLESTR szDevClassNetId = NULL;
StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
diff --git a/src/tapctl/tap.h b/src/tapctl/tap.h
index ee5fb09..a1350a5 100644
--- a/src/tapctl/tap.h
+++ b/src/tapctl/tap.h
@@ -100,6 +100,8 @@
tap_enable_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _In_
BOOL bEnable,
_Inout_ LPBOOL pbRebootRequired);
+BOOL
+tap_is_valid_adapter_name(LPCWSTR name);
/**
* Sets adapter name.
--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1335?usp=email
To unsubscribe, or for help writing mail filters, visit
http://gerrit.openvpn.net/settings?usp=email
Gerrit-MessageType: newpatchset
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I4d822bf7ae5f037e2414778e8fa74eca5e289e65
Gerrit-Change-Number: 1335
Gerrit-PatchSet: 3
Gerrit-Owner: stipa <[email protected]>
Gerrit-Reviewer: flichtenheld <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
Gerrit-Attention: flichtenheld <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel