OpenVPN
main.c
Go to the documentation of this file.
1 /*
2  * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
3  * https://community.openvpn.net/openvpn/wiki/Tapctl
4  *
5  * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
6  * Copyright (C) 2018-2024 Simon Rozman <simon@rozman.si>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include "tap.h"
27 #include "error.h"
28 
29 #include <objbase.h>
30 #include <setupapi.h>
31 #include <stdio.h>
32 #include <tchar.h>
33 
34 #ifdef _MSC_VER
35 #pragma comment(lib, "ole32.lib")
36 #pragma comment(lib, "setupapi.lib")
37 #endif
38 
39 
40 const TCHAR title_string[] =
41  TEXT(PACKAGE_NAME) TEXT(" ") TEXT(PACKAGE_VERSION)
42 ;
43 
44 static const TCHAR usage_message[] =
45  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
46  TEXT("\n")
47  TEXT("Usage:\n")
48  TEXT("\n")
49  TEXT("tapctl <command> [<command specific options>]\n")
50  TEXT("\n")
51  TEXT("Commands:\n")
52  TEXT("\n")
53  TEXT("create Create a new TUN/TAP adapter\n")
54  TEXT("list List TUN/TAP adapters\n")
55  TEXT("delete Delete specified network adapter\n")
56  TEXT("help Display this text\n")
57  TEXT("\n")
58  TEXT("Hint: Use \"tapctl help <command>\" to display help for particular command.\n")
59 ;
60 
61 static const TCHAR usage_message_create[] =
62  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
63  TEXT("\n")
64  TEXT("Creates a new TUN/TAP adapter\n")
65  TEXT("\n")
66  TEXT("Usage:\n")
67  TEXT("\n")
68  TEXT("tapctl create [<options>]\n")
69  TEXT("\n")
70  TEXT("Options:\n")
71  TEXT("\n")
72  TEXT("--name <name> Set TUN/TAP adapter name. Should the adapter with given name \n")
73  TEXT(" already exist, an error is returned. If this option is not \n")
74  TEXT(" specified, a default adapter name is chosen by Windows. \n")
75  TEXT(" Note: This name can also be specified as OpenVPN's --dev-node \n")
76  TEXT(" option. \n")
77  TEXT("--hwid <hwid> Adapter hardware ID. Default value is root\\tap0901, which \n")
78  TEXT(" describes tap-windows6 driver. To work with wintun or ovpn-dco \n")
79  TEXT(" driver, specify 'wintun' or 'ovpn-dco'. \n")
80  TEXT("\n")
81  TEXT("Output:\n")
82  TEXT("\n")
83  TEXT("This command prints newly created TUN/TAP adapter's GUID to stdout. \n")
84 ;
85 
86 static const TCHAR usage_message_list[] =
87  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
88  TEXT("\n")
89  TEXT("Lists TUN/TAP adapters\n")
90  TEXT("\n")
91  TEXT("Usage:\n")
92  TEXT("\n")
93  TEXT("tapctl list\n")
94  TEXT("\n")
95  TEXT("Options:\n")
96  TEXT("\n")
97  TEXT("--hwid <hwid> Adapter hardware ID. By default, root\\tap0901, tap0901, wintun and \n")
98  TEXT(" ovpn-dco adapters are listed. Use this switch to limit the list.\n")
99  TEXT("\n")
100  TEXT("Output:\n")
101  TEXT("\n")
102  TEXT("This command prints all TUN/TAP adapters to stdout. \n")
103 ;
104 
105 static const TCHAR usage_message_delete[] =
106  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
107  TEXT("\n")
108  TEXT("Deletes the specified network adapter\n")
109  TEXT("\n")
110  TEXT("Usage:\n")
111  TEXT("\n")
112  TEXT("tapctl delete <adapter GUID | adapter name>\n")
113 ;
114 
115 
119 static void
120 usage(void)
121 {
122  _ftprintf(stderr,
124  title_string);
125 }
126 
130 static BOOL
131 is_adapter_name_available(LPCTSTR name, struct tap_adapter_node *adapter_list, BOOL log)
132 {
133  for (struct tap_adapter_node *a = adapter_list; a; a = a->pNext)
134  {
135  if (_tcsicmp(name, a->szName) == 0)
136  {
137  if (log)
138  {
139  LPOLESTR adapter_id = NULL;
140  StringFromIID((REFIID)&a->guid, &adapter_id);
141  _ftprintf(stderr, TEXT("Adapter \"%") TEXT(PRIsLPTSTR) TEXT("\" already exists (GUID %")
142  TEXT(PRIsLPOLESTR) TEXT(").\n"), a->szName, adapter_id);
143  CoTaskMemFree(adapter_id);
144  }
145 
146  return FALSE;
147  }
148  }
149 
150  return TRUE;
151 }
152 
157 static LPTSTR
158 get_unique_adapter_name(LPCTSTR hwid, struct tap_adapter_node *adapter_list)
159 {
160  if (hwid == NULL)
161  {
162  return NULL;
163  }
164 
165  LPCTSTR base_name;
166  if (_tcsicmp(hwid, TEXT("ovpn-dco")) == 0)
167  {
168  base_name = TEXT("OpenVPN Data Channel Offload");
169  }
170  else if (_tcsicmp(hwid, TEXT("wintun")) == 0)
171  {
172  base_name = TEXT("OpenVPN Wintun");
173  }
174  else if (_tcsicmp(hwid, TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID)) == 0)
175  {
176  base_name = TEXT("OpenVPN TAP-Windows6");
177  }
178  else
179  {
180  return NULL;
181  }
182 
183  if (is_adapter_name_available(base_name, adapter_list, FALSE))
184  {
185  return _tcsdup(base_name);
186  }
187 
188  size_t name_len = _tcslen(base_name) + 10;
189  LPTSTR name = malloc(name_len * sizeof(TCHAR));
190  if (name == NULL)
191  {
192  return NULL;
193  }
194  for (int i = 1; i < 100; ++i)
195  {
196  _stprintf_s(name, name_len, TEXT("%ls #%d"), base_name, i);
197 
198  if (is_adapter_name_available(name, adapter_list, FALSE))
199  {
200  return name;
201  }
202  }
203 
204  return NULL;
205 }
206 
210 int __cdecl
211 _tmain(int argc, LPCTSTR argv[])
212 {
213  int iResult;
214  BOOL bRebootRequired = FALSE;
215 
216  /* Ask SetupAPI to keep quiet. */
217  SetupSetNonInteractiveMode(TRUE);
218 
219  if (argc < 2)
220  {
221  usage();
222  return 1;
223  }
224  else if (_tcsicmp(argv[1], TEXT("help")) == 0)
225  {
226  /* Output help. */
227  if (argc < 3)
228  {
229  usage();
230  }
231  else if (_tcsicmp(argv[2], TEXT("create")) == 0)
232  {
233  _ftprintf(stderr, usage_message_create, title_string);
234  }
235  else if (_tcsicmp(argv[2], TEXT("list")) == 0)
236  {
237  _ftprintf(stderr, usage_message_list, title_string);
238  }
239  else if (_tcsicmp(argv[2], TEXT("delete")) == 0)
240  {
241  _ftprintf(stderr, usage_message_delete, title_string);
242  }
243  else
244  {
245  _ftprintf(stderr, TEXT("Unknown command \"%") TEXT(PRIsLPTSTR)
246  TEXT("\". Please, use \"tapctl help\" to list supported commands.\n"), argv[2]);
247  }
248 
249  return 1;
250  }
251  else if (_tcsicmp(argv[1], TEXT("create")) == 0)
252  {
253  LPCTSTR szName = NULL;
254  LPCTSTR szHwId = TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID);
255 
256  /* Parse options. */
257  for (int i = 2; i < argc; i++)
258  {
259  if (_tcsicmp(argv[i], TEXT("--name")) == 0)
260  {
261  szName = argv[++i];
262  }
263  else if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
264  {
265  szHwId = argv[++i];
266  }
267  else
268  {
269  _ftprintf(stderr, TEXT("Unknown option \"%") TEXT(PRIsLPTSTR)
270  TEXT("\". Please, use \"tapctl help create\" to list supported options. Ignored.\n"),
271  argv[i]);
272  }
273  }
274 
275  /* Create TUN/TAP adapter. */
276  GUID guidAdapter;
277  LPOLESTR szAdapterId = NULL;
278  DWORD dwResult = tap_create_adapter(
279  NULL,
280  TEXT("Virtual Ethernet"),
281  szHwId,
282  &bRebootRequired,
283  &guidAdapter);
284  if (dwResult != ERROR_SUCCESS)
285  {
286  _ftprintf(stderr, TEXT("Creating TUN/TAP adapter failed (error 0x%x).\n"), dwResult);
287  iResult = 1; goto quit;
288  }
289 
290  /* Get existing network adapters. */
291  struct tap_adapter_node *pAdapterList = NULL;
292  dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
293  if (dwResult != ERROR_SUCCESS)
294  {
295  _ftprintf(stderr, TEXT("Enumerating adapters failed (error 0x%x).\n"), dwResult);
296  iResult = 1;
297  goto create_delete_adapter;
298  }
299 
300  LPTSTR adapter_name = szName ? _tcsdup(szName) : get_unique_adapter_name(szHwId, pAdapterList);
301  if (adapter_name)
302  {
303  /* Check for duplicates when name was specified,
304  * otherwise get_adapter_default_name() takes care of it */
305  if (szName && !is_adapter_name_available(adapter_name, pAdapterList, TRUE))
306  {
307  iResult = 1;
308  goto create_cleanup_pAdapterList;
309  }
310 
311  /* Rename the adapter. */
312  dwResult = tap_set_adapter_name(&guidAdapter, adapter_name, FALSE);
313  if (dwResult != ERROR_SUCCESS)
314  {
315  StringFromIID((REFIID)&guidAdapter, &szAdapterId);
316  _ftprintf(stderr, TEXT("Renaming TUN/TAP adapter %") TEXT(PRIsLPOLESTR)
317  TEXT(" to \"%") TEXT(PRIsLPTSTR) TEXT("\" failed (error 0x%x).\n"),
318  szAdapterId, adapter_name, dwResult);
319  CoTaskMemFree(szAdapterId);
320  iResult = 1; goto quit;
321  }
322  }
323 
324  iResult = 0;
325 
326 create_cleanup_pAdapterList:
327  free(adapter_name);
328 
329  tap_free_adapter_list(pAdapterList);
330  if (iResult)
331  {
332  goto create_delete_adapter;
333  }
334 
335  /* Output adapter GUID. */
336  StringFromIID((REFIID)&guidAdapter, &szAdapterId);
337  _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\n"), szAdapterId);
338  CoTaskMemFree(szAdapterId);
339 
340  iResult = 0; goto quit;
341 
342 create_delete_adapter:
344  NULL,
345  &guidAdapter,
346  &bRebootRequired);
347  iResult = 1; goto quit;
348  }
349  else if (_tcsicmp(argv[1], TEXT("list")) == 0)
350  {
351  TCHAR szzHwId[0x100] =
352  TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
353  TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
354  TEXT("Wintun\0")
355  TEXT("ovpn-dco\0");
356 
357  /* Parse options. */
358  for (int i = 2; i < argc; i++)
359  {
360  if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
361  {
362  memset(szzHwId, 0, sizeof(szzHwId));
363  ++i;
364  memcpy_s(szzHwId, sizeof(szzHwId) - 2*sizeof(TCHAR) /*requires double zero termination*/, argv[i], _tcslen(argv[i])*sizeof(TCHAR));
365  }
366  else
367  {
368  _ftprintf(stderr, TEXT("Unknown option \"%") TEXT(PRIsLPTSTR)
369  TEXT("\". Please, use \"tapctl help list\" to list supported options. Ignored.\n"),
370  argv[i]);
371  }
372  }
373 
374  /* Output list of adapters with given hardware ID. */
375  struct tap_adapter_node *pAdapterList = NULL;
376  DWORD dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
377  if (dwResult != ERROR_SUCCESS)
378  {
379  _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
380  iResult = 1; goto quit;
381  }
382 
383  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
384  {
385  LPOLESTR szAdapterId = NULL;
386  StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
387  _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\t%")
388  TEXT(PRIsLPTSTR) TEXT("\n"), szAdapterId, pAdapter->szName);
389  CoTaskMemFree(szAdapterId);
390  }
391 
392  iResult = 0;
393  tap_free_adapter_list(pAdapterList);
394  }
395  else if (_tcsicmp(argv[1], TEXT("delete")) == 0)
396  {
397  if (argc < 3)
398  {
399  _ftprintf(stderr, TEXT("Missing adapter GUID or name. Please, use \"tapctl help delete\" for usage info.\n"));
400  return 1;
401  }
402 
403  GUID guidAdapter;
404  if (FAILED(IIDFromString(argv[2], (LPIID)&guidAdapter)))
405  {
406  /* The argument failed to covert to GUID. Treat it as the adapter name. */
407  struct tap_adapter_node *pAdapterList = NULL;
408  DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
409  if (dwResult != ERROR_SUCCESS)
410  {
411  _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
412  iResult = 1; goto quit;
413  }
414 
415  for (struct tap_adapter_node *pAdapter = pAdapterList;; pAdapter = pAdapter->pNext)
416  {
417  if (pAdapter == NULL)
418  {
419  _ftprintf(stderr, TEXT("\"%") TEXT(PRIsLPTSTR) TEXT("\" adapter not found.\n"), argv[2]);
420  iResult = 1; goto delete_cleanup_pAdapterList;
421  }
422  else if (_tcsicmp(argv[2], pAdapter->szName) == 0)
423  {
424  memcpy(&guidAdapter, &pAdapter->guid, sizeof(GUID));
425  break;
426  }
427  }
428 
429  iResult = 0;
430 
431 delete_cleanup_pAdapterList:
432  tap_free_adapter_list(pAdapterList);
433  if (iResult)
434  {
435  goto quit;
436  }
437  }
438 
439  /* Delete the network adapter. */
440  DWORD dwResult = tap_delete_adapter(
441  NULL,
442  &guidAdapter,
443  &bRebootRequired);
444  if (dwResult != ERROR_SUCCESS)
445  {
446  _ftprintf(stderr, TEXT("Deleting adapter \"%") TEXT(PRIsLPTSTR)
447  TEXT("\" failed (error 0x%x).\n"), argv[2], dwResult);
448  iResult = 1; goto quit;
449  }
450 
451  iResult = 0; goto quit;
452  }
453  else
454  {
455  _ftprintf(stderr, TEXT("Unknown command \"%") TEXT(PRIsLPTSTR)
456  TEXT("\". Please, use \"tapctl help\" to list supported commands.\n"), argv[1]);
457  return 1;
458  }
459 
460 quit:
461  if (bRebootRequired)
462  {
463  _ftprintf(stderr, TEXT("A system reboot is required.\n"));
464  }
465 
466  return iResult;
467 }
468 
469 
470 bool
471 dont_mute(unsigned int flags)
472 {
473  UNREFERENCED_PARAMETER(flags);
474 
475  return true;
476 }
477 
478 
479 void
480 x_msg_va(const unsigned int flags, const char *format, va_list arglist)
481 {
482  /* Output message string. Note: Message strings don't contain line terminators. */
483  vfprintf(stderr, format, arglist);
484  _ftprintf(stderr, TEXT("\n"));
485 
486  if ((flags & M_ERRNO) != 0)
487  {
488  /* Output system error message (if possible). */
489  DWORD dwResult = GetLastError();
490  LPTSTR szErrMessage = NULL;
491  if (FormatMessage(
492  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
493  0,
494  dwResult,
495  0,
496  (LPTSTR)&szErrMessage,
497  0,
498  NULL) && szErrMessage)
499  {
500  /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
501  for (size_t i = 0, i_last = 0;; i++)
502  {
503  if (szErrMessage[i])
504  {
505  if (!_istspace(szErrMessage[i]))
506  {
507  i_last = i + 1;
508  }
509  }
510  else
511  {
512  szErrMessage[i_last] = 0;
513  break;
514  }
515  }
516 
517  /* Output error message. */
518  _ftprintf(stderr, TEXT("Error 0x%x: %") TEXT(PRIsLPTSTR) TEXT("\n"), dwResult, szErrMessage);
519 
520  LocalFree(szErrMessage);
521  }
522  else
523  {
524  _ftprintf(stderr, TEXT("Error 0x%x\n"), dwResult);
525  }
526  }
527 }
tap_adapter_node::pNext
struct tap_adapter_node * pNext
Adapter name.
Definition: tap.h:142
PRIsLPTSTR
#define PRIsLPTSTR
Definition: basic.h:29
M_ERRNO
#define M_ERRNO
Definition: error.h:94
get_unique_adapter_name
static LPTSTR get_unique_adapter_name(LPCTSTR hwid, struct tap_adapter_node *adapter_list)
Returns unique adapter name based on hwid or NULL if name cannot be generated.
Definition: main.c:158
PACKAGE_NAME
#define PACKAGE_NAME
Definition: config.h:492
argv
Definition: argv.h:35
_tmain
int __cdecl _tmain(int argc, LPCTSTR argv[])
Program entry point.
Definition: main.c:211
PACKAGE_VERSION
#define PACKAGE_VERSION
Definition: config.h:504
tap_delete_adapter
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition: tap.c:1005
usage_message_create
static const TCHAR usage_message_create[]
Definition: main.c:61
tap_adapter_node::szName
LPTSTR szName
Device hardware ID(s)
Definition: tap.h:140
tap_free_adapter_list
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition: tap.c:1355
dont_mute
bool dont_mute(unsigned int flags)
Check muting filter.
Definition: main.c:471
usage
static void usage(void)
Print the help message.
Definition: main.c:120
options
Definition: options.h:236
tap.h
usage_message
static const TCHAR usage_message[]
Definition: main.c:44
down_root_context::command
char ** command
Definition: down-root.c:78
tap_adapter_node
Network adapter list node.
Definition: tap.h:136
usage_message_delete
static const TCHAR usage_message_delete[]
Definition: main.c:105
PRIsLPOLESTR
#define PRIsLPOLESTR
Definition: basic.h:30
error.h
x_msg_va
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition: main.c:480
title_string
const TCHAR title_string[]
Definition: main.c:40
config.h
is_adapter_name_available
static BOOL is_adapter_name_available(LPCTSTR name, struct tap_adapter_node *adapter_list, BOOL log)
Checks if adapter with given name doesn't already exist.
Definition: main.c:131
tap_create_adapter
DWORD tap_create_adapter(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szDeviceDescription, _In_ LPCTSTR szHwId, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidAdapter)
Creates a TUN/TAP adapter.
Definition: tap.c:723
tap_set_adapter_name
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCTSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition: tap.c:1063
tap_list_adapters
DWORD tap_list_adapters(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szzHwIDs, _Out_ struct tap_adapter_node **ppAdapter)
Creates a list of existing network adapters.
Definition: tap.c:1146
TAP_WIN_COMPONENT_ID
#define TAP_WIN_COMPONENT_ID
Definition: config.h:546
usage_message_list
static const TCHAR usage_message_list[]
Definition: main.c:86