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