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-2021 OpenVPN Inc <sales@openvpn.net>
6  * Copyright (C) 2018-2021 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 #elif defined(_MSC_VER)
25 #include <config-msvc.h>
26 #endif
27 #ifdef HAVE_CONFIG_VERSION_H
28 #include <config-version.h>
29 #endif
30 
31 #include "tap.h"
32 #include "error.h"
33 
34 #include <objbase.h>
35 #include <setupapi.h>
36 #include <stdio.h>
37 #include <tchar.h>
38 
39 #ifdef _MSC_VER
40 #pragma comment(lib, "ole32.lib")
41 #pragma comment(lib, "setupapi.lib")
42 #endif
43 
44 
45 const TCHAR title_string[] =
46  TEXT(PACKAGE_NAME) TEXT(" ") TEXT(PACKAGE_VERSION)
47  TEXT(" built on ") TEXT(__DATE__)
48 ;
49 
50 static const TCHAR usage_message[] =
51  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
52  TEXT("\n")
53  TEXT("Usage:\n")
54  TEXT("\n")
55  TEXT("tapctl <command> [<command specific options>]\n")
56  TEXT("\n")
57  TEXT("Commands:\n")
58  TEXT("\n")
59  TEXT("create Create a new TUN/TAP adapter\n")
60  TEXT("list List TUN/TAP adapters\n")
61  TEXT("delete Delete specified network adapter\n")
62  TEXT("help Display this text\n")
63  TEXT("\n")
64  TEXT("Hint: Use \"tapctl help <command>\" to display help for particular command.\n")
65 ;
66 
67 static const TCHAR usage_message_create[] =
68  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
69  TEXT("\n")
70  TEXT("Creates a new TUN/TAP adapter\n")
71  TEXT("\n")
72  TEXT("Usage:\n")
73  TEXT("\n")
74  TEXT("tapctl create [<options>]\n")
75  TEXT("\n")
76  TEXT("Options:\n")
77  TEXT("\n")
78  TEXT("--name <name> Set TUN/TAP adapter name. Should the adapter with given name \n")
79  TEXT(" already exist, an error is returned. If this option is not \n")
80  TEXT(" specified, a default adapter name is chosen by Windows. \n")
81  TEXT(" Note: This name can also be specified as OpenVPN's --dev-node \n")
82  TEXT(" option. \n")
83  TEXT("--hwid <hwid> Adapter hardware ID. Default value is root\\tap0901, which \n")
84  TEXT(" describes tap-windows6 driver. To work with wintun or ovpn-dco \n")
85  TEXT(" driver, specify 'wintun' or 'ovpn-dco'. \n")
86  TEXT("\n")
87  TEXT("Output:\n")
88  TEXT("\n")
89  TEXT("This command prints newly created TUN/TAP adapter's GUID to stdout. \n")
90 ;
91 
92 static const TCHAR usage_message_list[] =
93  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
94  TEXT("\n")
95  TEXT("Lists TUN/TAP adapters\n")
96  TEXT("\n")
97  TEXT("Usage:\n")
98  TEXT("\n")
99  TEXT("tapctl list\n")
100  TEXT("\n")
101  TEXT("Options:\n")
102  TEXT("\n")
103  TEXT("--hwid <hwid> Adapter hardware ID. By default, root\\tap0901, tap0901, wintun and \n")
104  TEXT(" ovpn-dco adapters are listed. Use this switch to limit the list.\n")
105  TEXT("\n")
106  TEXT("Output:\n")
107  TEXT("\n")
108  TEXT("This command prints all TUN/TAP adapters to stdout. \n")
109 ;
110 
111 static const TCHAR usage_message_delete[] =
112  TEXT("%") TEXT(PRIsLPTSTR) TEXT("\n")
113  TEXT("\n")
114  TEXT("Deletes the specified network adapter\n")
115  TEXT("\n")
116  TEXT("Usage:\n")
117  TEXT("\n")
118  TEXT("tapctl delete <adapter GUID | adapter name>\n")
119 ;
120 
121 
125 static void
126 usage(void)
127 {
128  _ftprintf(stderr,
130  title_string);
131 }
132 
133 
137 int __cdecl
138 _tmain(int argc, LPCTSTR argv[])
139 {
140  int iResult;
141  BOOL bRebootRequired = FALSE;
142 
143  /* Ask SetupAPI to keep quiet. */
144  SetupSetNonInteractiveMode(TRUE);
145 
146  if (argc < 2)
147  {
148  usage();
149  return 1;
150  }
151  else if (_tcsicmp(argv[1], TEXT("help")) == 0)
152  {
153  /* Output help. */
154  if (argc < 3)
155  {
156  usage();
157  }
158  else if (_tcsicmp(argv[2], TEXT("create")) == 0)
159  {
160  _ftprintf(stderr, usage_message_create, title_string);
161  }
162  else if (_tcsicmp(argv[2], TEXT("list")) == 0)
163  {
164  _ftprintf(stderr, usage_message_list, title_string);
165  }
166  else if (_tcsicmp(argv[2], TEXT("delete")) == 0)
167  {
168  _ftprintf(stderr, usage_message_delete, title_string);
169  }
170  else
171  {
172  _ftprintf(stderr, TEXT("Unknown command \"%") TEXT(PRIsLPTSTR)
173  TEXT("\". Please, use \"tapctl help\" to list supported commands.\n"), argv[2]);
174  }
175 
176  return 1;
177  }
178  else if (_tcsicmp(argv[1], TEXT("create")) == 0)
179  {
180  LPCTSTR szName = NULL;
181  LPCTSTR szHwId = TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID);
182 
183  /* Parse options. */
184  for (int i = 2; i < argc; i++)
185  {
186  if (_tcsicmp(argv[i], TEXT("--name")) == 0)
187  {
188  szName = argv[++i];
189  }
190  else
191  if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
192  {
193  szHwId = argv[++i];
194  }
195  else
196  {
197  _ftprintf(stderr, TEXT("Unknown option \"%") TEXT(PRIsLPTSTR)
198  TEXT("\". Please, use \"tapctl help create\" to list supported options. Ignored.\n"),
199  argv[i]);
200  }
201  }
202 
203  /* Create TUN/TAP adapter. */
204  GUID guidAdapter;
205  LPOLESTR szAdapterId = NULL;
206  DWORD dwResult = tap_create_adapter(
207  NULL,
208  TEXT("Virtual Ethernet"),
209  szHwId,
210  &bRebootRequired,
211  &guidAdapter);
212  if (dwResult != ERROR_SUCCESS)
213  {
214  _ftprintf(stderr, TEXT("Creating TUN/TAP adapter failed (error 0x%x).\n"), dwResult);
215  iResult = 1; goto quit;
216  }
217 
218  if (szName)
219  {
220  /* Get existing network adapters. */
221  struct tap_adapter_node *pAdapterList = NULL;
222  dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
223  if (dwResult != ERROR_SUCCESS)
224  {
225  _ftprintf(stderr, TEXT("Enumerating adapters failed (error 0x%x).\n"), dwResult);
226  iResult = 1; goto create_delete_adapter;
227  }
228 
229  /* Check for duplicates. */
230  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
231  {
232  if (_tcsicmp(szName, pAdapter->szName) == 0)
233  {
234  StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
235  _ftprintf(stderr, TEXT("Adapter \"%") TEXT(PRIsLPTSTR) TEXT("\" already exists (GUID %")
236  TEXT(PRIsLPOLESTR) TEXT(").\n"), pAdapter->szName, szAdapterId);
237  CoTaskMemFree(szAdapterId);
238  iResult = 1; goto create_cleanup_pAdapterList;
239  }
240  }
241 
242  /* Rename the adapter. */
243  dwResult = tap_set_adapter_name(&guidAdapter, szName, FALSE);
244  if (dwResult != ERROR_SUCCESS)
245  {
246  StringFromIID((REFIID)&guidAdapter, &szAdapterId);
247  _ftprintf(stderr, TEXT("Renaming TUN/TAP adapter %") TEXT(PRIsLPOLESTR)
248  TEXT(" to \"%") TEXT(PRIsLPTSTR) TEXT("\" failed (error 0x%x).\n"),
249  szAdapterId, szName, dwResult);
250  CoTaskMemFree(szAdapterId);
251  iResult = 1; goto quit;
252  }
253 
254  iResult = 0;
255 
256 create_cleanup_pAdapterList:
257  tap_free_adapter_list(pAdapterList);
258  if (iResult)
259  {
260  goto create_delete_adapter;
261  }
262  }
263 
264  /* Output adapter GUID. */
265  StringFromIID((REFIID)&guidAdapter, &szAdapterId);
266  _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\n"), szAdapterId);
267  CoTaskMemFree(szAdapterId);
268 
269  iResult = 0; goto quit;
270 
271 create_delete_adapter:
273  NULL,
274  &guidAdapter,
275  &bRebootRequired);
276  iResult = 1; goto quit;
277  }
278  else if (_tcsicmp(argv[1], TEXT("list")) == 0)
279  {
280  TCHAR szzHwId[0x100] =
281  TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
282  TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
283  TEXT("Wintun\0")
284  TEXT("ovpn-dco\0");
285 
286  /* Parse options. */
287  for (int i = 2; i < argc; i++)
288  {
289  if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
290  {
291  memset(szzHwId, 0, sizeof(szzHwId));
292  ++i;
293  memcpy_s(szzHwId, sizeof(szzHwId) - 2*sizeof(TCHAR) /*requires double zero termination*/, argv[i], _tcslen(argv[i])*sizeof(TCHAR));
294  }
295  else
296  {
297  _ftprintf(stderr, TEXT("Unknown option \"%") TEXT(PRIsLPTSTR)
298  TEXT("\". Please, use \"tapctl help list\" to list supported options. Ignored.\n"),
299  argv[i]);
300  }
301  }
302 
303  /* Output list of adapters with given hardware ID. */
304  struct tap_adapter_node *pAdapterList = NULL;
305  DWORD dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
306  if (dwResult != ERROR_SUCCESS)
307  {
308  _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
309  iResult = 1; goto quit;
310  }
311 
312  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
313  {
314  LPOLESTR szAdapterId = NULL;
315  StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
316  _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\t%")
317  TEXT(PRIsLPTSTR) TEXT("\n"), szAdapterId, pAdapter->szName);
318  CoTaskMemFree(szAdapterId);
319  }
320 
321  iResult = 0;
322  tap_free_adapter_list(pAdapterList);
323  }
324  else if (_tcsicmp(argv[1], TEXT("delete")) == 0)
325  {
326  if (argc < 3)
327  {
328  _ftprintf(stderr, TEXT("Missing adapter GUID or name. Please, use \"tapctl help delete\" for usage info.\n"));
329  return 1;
330  }
331 
332  GUID guidAdapter;
333  if (FAILED(IIDFromString(argv[2], (LPIID)&guidAdapter)))
334  {
335  /* The argument failed to covert to GUID. Treat it as the adapter name. */
336  struct tap_adapter_node *pAdapterList = NULL;
337  DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
338  if (dwResult != ERROR_SUCCESS)
339  {
340  _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
341  iResult = 1; goto quit;
342  }
343 
344  for (struct tap_adapter_node *pAdapter = pAdapterList;; pAdapter = pAdapter->pNext)
345  {
346  if (pAdapter == NULL)
347  {
348  _ftprintf(stderr, TEXT("\"%") TEXT(PRIsLPTSTR) TEXT("\" adapter not found.\n"), argv[2]);
349  iResult = 1; goto delete_cleanup_pAdapterList;
350  }
351  else if (_tcsicmp(argv[2], pAdapter->szName) == 0)
352  {
353  memcpy(&guidAdapter, &pAdapter->guid, sizeof(GUID));
354  break;
355  }
356  }
357 
358  iResult = 0;
359 
360 delete_cleanup_pAdapterList:
361  tap_free_adapter_list(pAdapterList);
362  if (iResult)
363  {
364  goto quit;
365  }
366  }
367 
368  /* Delete the network adapter. */
369  DWORD dwResult = tap_delete_adapter(
370  NULL,
371  &guidAdapter,
372  &bRebootRequired);
373  if (dwResult != ERROR_SUCCESS)
374  {
375  _ftprintf(stderr, TEXT("Deleting adapter \"%") TEXT(PRIsLPTSTR)
376  TEXT("\" failed (error 0x%x).\n"), argv[2], dwResult);
377  iResult = 1; goto quit;
378  }
379 
380  iResult = 0; goto quit;
381  }
382  else
383  {
384  _ftprintf(stderr, TEXT("Unknown command \"%") TEXT(PRIsLPTSTR)
385  TEXT("\". Please, use \"tapctl help\" to list supported commands.\n"), argv[1]);
386  return 1;
387  }
388 
389 quit:
390  if (bRebootRequired)
391  {
392  _ftprintf(stderr, TEXT("A system reboot is required.\n"));
393  }
394 
395  return iResult;
396 }
397 
398 
399 bool
400 dont_mute(unsigned int flags)
401 {
402  UNREFERENCED_PARAMETER(flags);
403 
404  return true;
405 }
406 
407 
408 void
409 x_msg_va(const unsigned int flags, const char *format, va_list arglist)
410 {
411  /* Output message string. Note: Message strings don't contain line terminators. */
412  vfprintf(stderr, format, arglist);
413  _ftprintf(stderr, TEXT("\n"));
414 
415  if ((flags & M_ERRNO) != 0)
416  {
417  /* Output system error message (if possible). */
418  DWORD dwResult = GetLastError();
419  LPTSTR szErrMessage = NULL;
420  if (FormatMessage(
421  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
422  0,
423  dwResult,
424  0,
425  (LPTSTR)&szErrMessage,
426  0,
427  NULL) && szErrMessage)
428  {
429  /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
430  for (size_t i = 0, i_last = 0;; i++)
431  {
432  if (szErrMessage[i])
433  {
434  if (!_istspace(szErrMessage[i]))
435  {
436  i_last = i + 1;
437  }
438  }
439  else
440  {
441  szErrMessage[i_last] = 0;
442  break;
443  }
444  }
445 
446  /* Output error message. */
447  _ftprintf(stderr, TEXT("Error 0x%x: %") TEXT(PRIsLPTSTR) TEXT("\n"), dwResult, szErrMessage);
448 
449  LocalFree(szErrMessage);
450  }
451  else
452  {
453  _ftprintf(stderr, TEXT("Error 0x%x\n"), dwResult);
454  }
455  }
456 }
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCTSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition: tap.c:1065
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition: main.c:409
#define PRIsLPTSTR
Definition: basic.h:29
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition: tap.c:1007
static const TCHAR usage_message_create[]
Definition: main.c:67
char ** command
Definition: down-root.c:78
bool dont_mute(unsigned int flags)
Check muting filter.
Definition: main.c:400
list flags
static const TCHAR usage_message_delete[]
Definition: main.c:111
#define PACKAGE_NAME
Definition: config.h:730
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:725
const TCHAR title_string[]
Definition: main.c:45
#define PRIsLPOLESTR
Definition: basic.h:30
#define TAP_WIN_COMPONENT_ID
Definition: config.h:790
struct tap_adapter_node * pNext
Adapter name.
Definition: tap.h:142
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:1148
#define M_ERRNO
Definition: error.h:103
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition: tap.c:1357
int __cdecl _tmain(int argc, LPCTSTR argv[])
Program entry point.
Definition: main.c:138
static const TCHAR usage_message_list[]
Definition: main.c:92
static void usage(void)
Print the help message.
Definition: main.c:126
#define PACKAGE_VERSION
Definition: config.h:742
Network adapter list node.
Definition: tap.h:136
Definition: argv.h:35
static const TCHAR usage_message[]
Definition: main.c:50