diff options
Diffstat (limited to 'src/tapctl/main.c')
-rw-r--r-- | src/tapctl/main.c | 445 |
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); + } + } +} |