OpenVPN
main.c
Go to the documentation of this file.
1 /*
2  * tapctl -- Utility to manipulate TUN/TAP interfaces on Windows
3  * https://community.openvpn.net/openvpn/wiki/Tapctl
4  *
5  * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6  * Copyright (C) 2008-2013 David Sommerseth <dazo@users.sourceforge.net>
7  * Copyright (C) 2018 Simon Rozman <simon@rozman.si>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2
11  * as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #elif defined(_MSC_VER)
26 #include <config-msvc.h>
27 #endif
28 #ifdef HAVE_CONFIG_VERSION_H
29 #include <config-version.h>
30 #endif
31 
32 #include "tap.h"
33 #include "error.h"
34 
35 #include <objbase.h>
36 #include <setupapi.h>
37 #include <stdio.h>
38 #include <tchar.h>
39 
40 #ifdef _MSC_VER
41 #pragma comment(lib, "ole32.lib")
42 #pragma comment(lib, "setupapi.lib")
43 #endif
44 
45 
46 const TCHAR title_string[] =
47  TEXT(PACKAGE_NAME) TEXT(" ") TEXT(PACKAGE_VERSION)
48  TEXT(" built on ") TEXT(__DATE__)
49 ;
50 
51 static const TCHAR usage_message[] =
52  TEXT("%s\n")
53  TEXT("\n")
54  TEXT("Usage:\n")
55  TEXT("\n")
56  TEXT("tapctl <command> [<command specific options>]\n")
57  TEXT("\n")
58  TEXT("Commands:\n")
59  TEXT("\n")
60  TEXT("create Create a new TUN/TAP interface\n")
61  TEXT("list List TUN/TAP interfaces\n")
62  TEXT("delete Delete specified network interface\n")
63  TEXT("help Display this text\n")
64  TEXT("\n")
65  TEXT("Hint: Use \"tapctl help <command>\" to display help for particular command.\n")
66 ;
67 
68 static const TCHAR usage_message_create[] =
69  TEXT("%s\n")
70  TEXT("\n")
71  TEXT("Creates a new TUN/TAP interface\n")
72  TEXT("\n")
73  TEXT("Usage:\n")
74  TEXT("\n")
75  TEXT("tapctl create [<options>]\n")
76  TEXT("\n")
77  TEXT("Options:\n")
78  TEXT("\n")
79  TEXT("--name <name> Set TUN/TAP interface name. Should the interface with given name\n")
80  TEXT(" already exist, an error is returned. If this option is not \n")
81  TEXT(" specified, a default interface name is chosen by Windows. \n")
82  TEXT(" Note: This name can also be specified as OpenVPN's --dev-node \n")
83  TEXT(" option. \n")
84  TEXT("\n")
85  TEXT("Output:\n")
86  TEXT("\n")
87  TEXT("This command prints newly created TUN/TAP interface's GUID to stdout. \n")
88 ;
89 
90 static const TCHAR usage_message_list[] =
91  TEXT("%s\n")
92  TEXT("\n")
93  TEXT("Lists TUN/TAP interfaces\n")
94  TEXT("\n")
95  TEXT("Usage:\n")
96  TEXT("\n")
97  TEXT("tapctl list\n")
98  TEXT("\n")
99  TEXT("Output:\n")
100  TEXT("\n")
101  TEXT("This command prints all TUN/TAP interfaces to stdout. \n")
102 ;
103 
104 static const TCHAR usage_message_delete[] =
105  TEXT("%s\n")
106  TEXT("\n")
107  TEXT("Deletes the specified network interface\n")
108  TEXT("\n")
109  TEXT("Usage:\n")
110  TEXT("\n")
111  TEXT("tapctl delete <interface GUID | interface name>\n")
112 ;
113 
114 
118 static void
119 usage(void)
120 {
121  _ftprintf(stderr,
123  title_string);
124 }
125 
126 
130 int __cdecl
131 _tmain(int argc, LPCTSTR argv[])
132 {
133  int iResult;
134  BOOL bRebootRequired = FALSE;
135 
136  /* Ask SetupAPI to keep quiet. */
137  SetupSetNonInteractiveMode(TRUE);
138 
139  if (argc < 2)
140  {
141  usage();
142  return 1;
143  }
144  else if (_tcsicmp(argv[1], TEXT("help")) == 0)
145  {
146  /* Output help. */
147  if (argc < 3)
148  {
149  usage();
150  }
151  else if (_tcsicmp(argv[2], TEXT("create")) == 0)
152  {
153  _ftprintf(stderr, usage_message_create, title_string);
154  }
155  else if (_tcsicmp(argv[2], TEXT("list")) == 0)
156  {
157  _ftprintf(stderr, usage_message_list, title_string);
158  }
159  else if (_tcsicmp(argv[2], TEXT("delete")) == 0)
160  {
161  _ftprintf(stderr, usage_message_delete, title_string);
162  }
163  else
164  {
165  _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[2]);
166  }
167 
168  return 1;
169  }
170  else if (_tcsicmp(argv[1], TEXT("create")) == 0)
171  {
172  LPCTSTR szName = NULL;
173 
174  /* Parse options. */
175  for (int i = 2; i < argc; i++)
176  {
177  if (_tcsicmp(argv[i], TEXT("--name")) == 0)
178  {
179  szName = argv[++i];
180  }
181  else
182  {
183  _ftprintf(stderr, TEXT("Unknown option \"%s\". Please, use \"tapctl help create\" to list supported options. Ignored.\n"), argv[i]);
184  }
185  }
186 
187  /* Create TUN/TAP interface. */
188  GUID guidInterface;
189  LPOLESTR szInterfaceId = NULL;
190  DWORD dwResult = tap_create_interface(
191  NULL,
192  TEXT("Virtual Ethernet"),
193  &bRebootRequired,
194  &guidInterface);
195  if (dwResult != ERROR_SUCCESS)
196  {
197  _ftprintf(stderr, TEXT("Creating TUN/TAP interface failed (error 0x%x).\n"), dwResult);
198  iResult = 1; goto quit;
199  }
200 
201  if (szName)
202  {
203  /* Get the list of all available interfaces. */
204  struct tap_interface_node *pInterfaceList = NULL;
205  dwResult = tap_list_interfaces(NULL, &pInterfaceList, TRUE);
206  if (dwResult != ERROR_SUCCESS)
207  {
208  _ftprintf(stderr, TEXT("Enumerating interfaces failed (error 0x%x).\n"), dwResult);
209  iResult = 1; goto create_delete_interface;
210  }
211 
212  /* Check for duplicates. */
213  for (struct tap_interface_node *pInterface = pInterfaceList; pInterface; pInterface = pInterface->pNext)
214  {
215  if (_tcsicmp(szName, pInterface->szName) == 0)
216  {
217  StringFromIID((REFIID)&pInterface->guid, &szInterfaceId);
218  _ftprintf(stderr, TEXT("Interface \"%s\" already exists (GUID %") TEXT(PRIsLPOLESTR) TEXT(").\n"), pInterface->szName, szInterfaceId);
219  CoTaskMemFree(szInterfaceId);
220  iResult = 1; goto create_cleanup_pInterfaceList;
221  }
222  }
223 
224  /* Rename the interface. */
225  dwResult = tap_set_interface_name(&guidInterface, szName);
226  if (dwResult != ERROR_SUCCESS)
227  {
228  StringFromIID((REFIID)&guidInterface, &szInterfaceId);
229  _ftprintf(stderr, TEXT("Renaming TUN/TAP interface %") TEXT(PRIsLPOLESTR) TEXT(" to \"%s\" failed (error 0x%x).\n"), szInterfaceId, szName, dwResult);
230  CoTaskMemFree(szInterfaceId);
231  iResult = 1; goto quit;
232  }
233 
234  iResult = 0;
235 
236 create_cleanup_pInterfaceList:
237  tap_free_interface_list(pInterfaceList);
238  if (iResult)
239  {
240  goto create_delete_interface;
241  }
242  }
243 
244  /* Output interface GUID. */
245  StringFromIID((REFIID)&guidInterface, &szInterfaceId);
246  _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\n"), szInterfaceId);
247  CoTaskMemFree(szInterfaceId);
248 
249  iResult = 0; goto quit;
250 
251 create_delete_interface:
253  NULL,
254  &guidInterface,
255  &bRebootRequired);
256  iResult = 1; goto quit;
257  }
258  else if (_tcsicmp(argv[1], TEXT("list")) == 0)
259  {
260  /* Output list of TUN/TAP interfaces. */
261  struct tap_interface_node *pInterfaceList = NULL;
262  DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList, FALSE);
263  if (dwResult != ERROR_SUCCESS)
264  {
265  _ftprintf(stderr, TEXT("Enumerating TUN/TAP interfaces failed (error 0x%x).\n"), dwResult);
266  iResult = 1; goto quit;
267  }
268 
269  for (struct tap_interface_node *pInterface = pInterfaceList; pInterface; pInterface = pInterface->pNext)
270  {
271  LPOLESTR szInterfaceId = NULL;
272  StringFromIID((REFIID)&pInterface->guid, &szInterfaceId);
273  _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\t%") TEXT(PRIsLPTSTR) TEXT("\n"), szInterfaceId, pInterface->szName);
274  CoTaskMemFree(szInterfaceId);
275  }
276 
277  iResult = 0;
278  tap_free_interface_list(pInterfaceList);
279  }
280  else if (_tcsicmp(argv[1], TEXT("delete")) == 0)
281  {
282  if (argc < 3)
283  {
284  _ftprintf(stderr, TEXT("Missing interface GUID or name. Please, use \"tapctl help delete\" for usage info.\n"));
285  return 1;
286  }
287 
288  GUID guidInterface;
289  if (FAILED(IIDFromString(argv[2], (LPIID)&guidInterface)))
290  {
291  /* The argument failed to covert to GUID. Treat it as the interface name. */
292  struct tap_interface_node *pInterfaceList = NULL;
293  DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList, FALSE);
294  if (dwResult != ERROR_SUCCESS)
295  {
296  _ftprintf(stderr, TEXT("Enumerating TUN/TAP interfaces failed (error 0x%x).\n"), dwResult);
297  iResult = 1; goto quit;
298  }
299 
300  for (struct tap_interface_node *pInterface = pInterfaceList;; pInterface = pInterface->pNext)
301  {
302  if (pInterface == NULL)
303  {
304  _ftprintf(stderr, TEXT("\"%s\" interface not found.\n"), argv[2]);
305  iResult = 1; goto delete_cleanup_pInterfaceList;
306  }
307  else if (_tcsicmp(argv[2], pInterface->szName) == 0)
308  {
309  memcpy(&guidInterface, &pInterface->guid, sizeof(GUID));
310  break;
311  }
312  }
313 
314  iResult = 0;
315 
316 delete_cleanup_pInterfaceList:
317  tap_free_interface_list(pInterfaceList);
318  if (iResult)
319  {
320  goto quit;
321  }
322  }
323 
324  /* Delete the network interface. */
325  DWORD dwResult = tap_delete_interface(
326  NULL,
327  &guidInterface,
328  &bRebootRequired);
329  if (dwResult != ERROR_SUCCESS)
330  {
331  _ftprintf(stderr, TEXT("Deleting interface \"%s\" failed (error 0x%x).\n"), argv[2], dwResult);
332  iResult = 1; goto quit;
333  }
334 
335  iResult = 0; goto quit;
336  }
337  else
338  {
339  _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[1]);
340  return 1;
341  }
342 
343 quit:
344  if (bRebootRequired)
345  {
346  _ftprintf(stderr, TEXT("A system reboot is required.\n"));
347  }
348 
349  return iResult;
350 }
351 
352 
353 bool
354 dont_mute(unsigned int flags)
355 {
356  UNREFERENCED_PARAMETER(flags);
357 
358  return true;
359 }
360 
361 
362 void
363 x_msg_va(const unsigned int flags, const char *format, va_list arglist)
364 {
365  /* Output message string. Note: Message strings don't contain line terminators. */
366  vfprintf(stderr, format, arglist);
367  _ftprintf(stderr, TEXT("\n"));
368 
369  if ((flags & M_ERRNO) != 0)
370  {
371  /* Output system error message (if possible). */
372  DWORD dwResult = GetLastError();
373  LPTSTR szErrMessage = NULL;
374  if (FormatMessage(
375  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
376  0,
377  dwResult,
378  0,
379  (LPTSTR)&szErrMessage,
380  0,
381  NULL) && szErrMessage)
382  {
383  /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
384  for (size_t i = 0, i_last = 0;; i++)
385  {
386  if (szErrMessage[i])
387  {
388  if (!_istspace(szErrMessage[i]))
389  {
390  i_last = i + 1;
391  }
392  }
393  else
394  {
395  szErrMessage[i_last] = 0;
396  break;
397  }
398  }
399 
400  /* Output error message. */
401  _ftprintf(stderr, TEXT("Error 0x%x: %s\n"), dwResult, szErrMessage);
402 
403  LocalFree(szErrMessage);
404  }
405  else
406  {
407  _ftprintf(stderr, TEXT("Error 0x%x\n"), dwResult);
408  }
409  }
410 }
LPCTSTR szName
Definition: openvpnmsica.c:67
DWORD tap_delete_interface(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidInterface, _Inout_ LPBOOL pbRebootRequired)
Deletes an interface.
Definition: tap.c:753
DWORD tap_set_interface_name(_In_ LPCGUID pguidInterface, _In_ LPCTSTR szName)
Sets interface name.
Definition: tap.c:878
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition: main.c:363
#define PRIsLPTSTR
Definition: basic.h:29
static const TCHAR usage_message_create[]
Definition: main.c:68
char ** command
Definition: down-root.c:78
bool dont_mute(unsigned int flags)
Check muting filter.
Definition: main.c:354
list flags
static const TCHAR usage_message_delete[]
Definition: main.c:104
#define PACKAGE_NAME
Definition: config.h:730
const TCHAR title_string[]
Definition: main.c:46
#define PRIsLPOLESTR
Definition: basic.h:30
struct tap_interface_node * pNext
Interface name.
Definition: tap.h:106
DWORD tap_list_interfaces(_In_opt_ HWND hwndParent, _Out_ struct tap_interface_node **ppInterface, _In_ BOOL bAll)
Creates a list of available network interfaces.
Definition: tap.c:954
#define M_ERRNO
Definition: error.h:99
int __cdecl _tmain(int argc, LPCTSTR argv[])
Program entry point.
Definition: main.c:131
DWORD tap_create_interface(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szDeviceDescription, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidInterface)
Creates a TUN/TAP interface.
Definition: tap.c:448
Network interface list node.
Definition: tap.h:100
static const TCHAR usage_message_list[]
Definition: main.c:90
void tap_free_interface_list(_In_ struct tap_interface_node *pInterfaceList)
Frees a list of network interfaces.
Definition: tap.c:1163
static void usage(void)
Print the help message.
Definition: main.c:119
#define PACKAGE_VERSION
Definition: config.h:742
Definition: argv.h:35
static const TCHAR usage_message[]
Definition: main.c:51