/* 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/Except.h"
#include "exception/WinFail.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
#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 struct Taps* get_all_taps(struct Allocator* alloc, struct Except* eh)
{
LONG status;
HKEY control_net_key;
DWORD len;
struct Taps* taps = NULL;
struct Taps* tail = NULL;
WinFail_check(eh, (
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) {
WinFail_fail(eh, "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);
WinFail_check(eh, (
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.
strncpy(name_data, "", sizeof (name_data));
} else if (status != ERROR_SUCCESS) {
WinFail_fail(eh, "RegQueryValueEx() for interface name failed", status);
} else {
if (name_type != REG_SZ) {
// Someone named an interface with a non-string
WinFail_fail(eh, "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);
return taps;
}
static int get_device_guid(char *name,
int name_size,
char *actual_name,
int actual_name_size,
struct Allocator* alloc,
struct Except* eh)
{
char buff[BUFF_SZ] = {0};
HANDLE handle;
struct Taps* taps = get_all_taps(alloc, eh);
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);
return 0;
}
taps = taps->next;
}
return -1;
}
struct TAPDevice* TAPDevice_find(const char* preferredName,
struct Except* eh,
struct Allocator* alloc)
{
char guid[BUFF_SZ] = {0};
char buff[BUFF_SZ] = {0};
if (preferredName != NULL) {
snprintf(buff, sizeof(buff), "%s", preferredName);
}
if (get_device_guid(guid, sizeof(guid), buff, sizeof(buff), alloc, eh)) {
return 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);
return out;
}