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