/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "util/Bits.h" #include "exception/Er.h" #include "exception/WinEr.h" #include "memory/Allocator.h" #include "interface/tuntap/windows/TAPDevice.h" #include "util/CString.h" /* * Portions of this code are copied from QEMU project which is licensed * under GPLv2 or greater, further contributions are licensed under GPLv3 * or greater. */ /* * TAP-Win32 -- A kernel driver to provide virtual tap device functionality * on win32. Originally derived from the CIPE-Win32 * project by Damion K. Wilson, with extensive modifications by * James Yonan. * * All source code which derives from the CIPE-Win32 project is * Copyright (C) Damion K. Wilson, 2003, and is released under the * GPL version 2 (see below). * * All other source code is Copyright (C) James Yonan, 2003-2004, * and is released under the GPL version 2 (see below). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "util/CString.h" #include #include #define NETWORK_ADAPTER_GUID "{4D36E972-E325-11CE-BFC1-08002BE10318}" #define ADAPTER_KEY \ "SYSTEM\\CurrentControlSet\\Control\\Class\\" NETWORK_ADAPTER_GUID #define NETWORK_CONNECTIONS_KEY \ "SYSTEM\\CurrentControlSet\\Control\\Network\\" NETWORK_ADAPTER_GUID #define USERMODEDEVICEDIR "\\\\.\\Global\\" #define TAPSUFFIX ".tap" #define BUFF_SZ 0x100 struct Taps { char guid[BUFF_SZ]; char name[BUFF_SZ]; struct Taps* next; }; static int is_tap_win32_dev(const char *guid) { HKEY netcard_key; LONG status; DWORD len; int i = 0; status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &netcard_key); if (status != ERROR_SUCCESS) { return FALSE; } for (;;) { char enum_name[256]; char unit_string[256]; HKEY unit_key; char component_id_string[] = "ComponentId"; char component_id[256]; char net_cfg_instance_id_string[] = "NetCfgInstanceId"; char net_cfg_instance_id[256]; DWORD data_type; len = sizeof (enum_name); status = RegEnumKeyEx( netcard_key, i, enum_name, &len, NULL, NULL, NULL, NULL); if (status == ERROR_NO_MORE_ITEMS) { break; } else if (status != ERROR_SUCCESS) { return FALSE; } snprintf (unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, enum_name); status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key); if (status != ERROR_SUCCESS) { return FALSE; } else { len = sizeof (component_id); status = RegQueryValueEx( unit_key, component_id_string, NULL, &data_type, (uint8_t*)component_id, &len); if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) { len = sizeof (net_cfg_instance_id); status = RegQueryValueEx( unit_key, net_cfg_instance_id_string, NULL, &data_type, (uint8_t*)net_cfg_instance_id, &len); if (status == ERROR_SUCCESS && data_type == REG_SZ) { if (!Bits_memcmp(component_id, "tap", CString_strlen("tap")) && !strcmp (net_cfg_instance_id, guid)) { RegCloseKey (unit_key); RegCloseKey (netcard_key); return TRUE; } } } RegCloseKey (unit_key); } ++i; } RegCloseKey (netcard_key); return FALSE; } static Er_DEFUN(struct Taps* get_all_taps(struct Allocator* alloc)) { LONG status; HKEY control_net_key; DWORD len; struct Taps* taps = NULL; struct Taps* tail = NULL; WinEr_check(alloc, ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &control_net_key) )); int i = 0; char enum_name[BUFF_SZ]; char connection_string[BUFF_SZ]; char name_data[BUFF_SZ]; const char name_string[] = "Name"; while (true) { HKEY connKey; DWORD name_type; Bits_memset(enum_name, 0, sizeof(enum_name)); Bits_memset(connection_string, 0, sizeof(connection_string)); Bits_memset(name_data, 0, sizeof(name_data)); len = sizeof (enum_name); status = RegEnumKeyEx(control_net_key, i, enum_name, &len, NULL, NULL, NULL, NULL); if (status == ERROR_NO_MORE_ITEMS) { break; } else if (status != ERROR_SUCCESS) { WinEr_fail(alloc, "RegEnumKeyEx() failed", status); } if (len != CString_strlen(NETWORK_ADAPTER_GUID)) { // extranious directory, eg: "Descriptions" ++i; continue; } snprintf(connection_string, sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name); WinEr_check(alloc, ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string, 0, KEY_READ, &connKey) )); // In Windows 10, some interface keys don't have names. We should keep // going and treat those interfaces as having empty string names. len = sizeof (name_data); status = RegQueryValueEx(connKey, name_string, NULL, &name_type, (uint8_t*)name_data, &len); if (status == ERROR_FILE_NOT_FOUND) { // The interface has no name. CString_safeStrncpy(name_data, "", sizeof (name_data)); } else if (status != ERROR_SUCCESS) { WinEr_fail(alloc, "RegQueryValueEx() for interface name failed", status); } else { if (name_type != REG_SZ) { // Someone named an interface with a non-string WinEr_fail(alloc, "RegQueryValueEx() name_type != REG_SZ", status); } } if (is_tap_win32_dev(enum_name)) { struct Taps* tap = Allocator_calloc(alloc, sizeof(struct Taps), 1); Bits_memcpy(tap->guid, enum_name, sizeof(enum_name)); Bits_memcpy(tap->name, name_data, sizeof(name_data)); if (!taps) { taps = tap; taps->next = NULL; } if (tail) { tail->next = tap; } tail = tap; } RegCloseKey(connKey); ++i; } RegCloseKey (control_net_key); Er_ret(taps); } static Er_DEFUN(int get_device_guid(char *name, int name_size, char *actual_name, int actual_name_size, struct Allocator* alloc)) { char buff[BUFF_SZ] = {0}; HANDLE handle; struct Taps* taps = Er(get_all_taps(alloc)); while (taps) { if (actual_name && CString_strcmp(actual_name, "") != 0) { if (CString_strcmp(taps->name, actual_name) != 0) { taps = taps->next; continue; } } snprintf(buff, sizeof(buff), USERMODEDEVICEDIR "%s" TAPSUFFIX, taps->guid); handle = CreateFile(buff, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); if (handle != INVALID_HANDLE_VALUE) { snprintf(name, name_size, "%s", taps->guid); if (actual_name && CString_strcmp(actual_name, "") == 0) { snprintf(actual_name, actual_name_size, "%s", taps->name); } CloseHandle(handle); Er_ret(0); } taps = taps->next; } Er_ret(-1); } Er_DEFUN(struct TAPDevice* TAPDevice_find(const char* preferredName, struct Allocator* alloc)) { char guid[BUFF_SZ] = {0}; char buff[BUFF_SZ] = {0}; if (preferredName != NULL) { snprintf(buff, sizeof(buff), "%s", preferredName); } int ret = Er(get_device_guid(guid, sizeof(guid), buff, sizeof(buff), alloc)); if (ret) { Er_ret(NULL); } struct TAPDevice* out = Allocator_malloc(alloc, sizeof(struct TAPDevice)); out->name = Allocator_malloc(alloc, CString_strlen(buff)+1); Bits_memcpy(out->name, buff, CString_strlen(buff)+1); snprintf(buff, sizeof(buff), USERMODEDEVICEDIR "%s" TAPSUFFIX, guid); out->path = Allocator_malloc(alloc, CString_strlen(buff)+1); Bits_memcpy(out->path, buff, CString_strlen(buff)+1); Er_ret(out); }