#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef CONFIG #define CONFIG "config.h" #endif // CONFIG #include CONFIG #include "helpers.h" #include "wintap.h" #ifndef NO_TAP #include "types.h" #include "endian.h" #include "output.h" #include "tap-windows.h" #include #if !_WIN32 #include #endif // !_WIN32 static char* szIpAddress = "10.10.10.9"; static char* szMask = "30"; static char* szTapName; static char *ActiveTapName, *AdapterClass; static char* szLeaseDuration = "1d"; static uint32_t IpAddress, Mask, Network, Broadcast, DhcpServer; // These are host-endian (=little-endian) for easier calculations static uint32_t Mtu; static uint_fast8_t Cidr; static HANDLE TapHandle; static TapDriverVersion_t DriverVersion; static IpPacket_t* IpPacket; static uint32_t DhcpLeaseDuration; static BOOL isAddressAssigned() { PMIB_IPADDRTABLE pIPAddrTable; DWORD dwSize = 0; BOOL result = FALSE; pIPAddrTable = (PMIB_IPADDRTABLE)vlmcsd_malloc(sizeof(MIB_IPADDRTABLE)); DWORD status = GetIpAddrTable(pIPAddrTable, &dwSize, 0); free(pIPAddrTable); if (status != ERROR_INSUFFICIENT_BUFFER) return FALSE; pIPAddrTable = (MIB_IPADDRTABLE *)vlmcsd_malloc(dwSize); if (GetIpAddrTable(pIPAddrTable, &dwSize, 0)) { free(pIPAddrTable); return FALSE; } PMIB_IPADDRROW row; for (row = pIPAddrTable->table; row < pIPAddrTable->table + pIPAddrTable->dwNumEntries; row++) { if ( row->dwAddr == BE32(IpAddress) && !(row->wType & (MIB_IPADDR_DELETED | MIB_IPADDR_DISCONNECTED | MIB_IPADDR_TRANSIENT)) ) { result = TRUE; break; } } free(pIPAddrTable); return result; } static void parseTapArgument(char* argument) { char* equalsignPosition = strchr(argument, (int)'='); char* slashPosition = strchr(argument, (int)'/'); char* colonPosition = strchr(argument, (int)':'); szTapName = argument; if (equalsignPosition) { *equalsignPosition = 0; szIpAddress = equalsignPosition + 1; } if (slashPosition) { *slashPosition = 0; szMask = slashPosition + 1; } if (colonPosition) { *colonPosition = 0; szLeaseDuration = colonPosition + 1; } IpAddress = BE32(inet_addr(szIpAddress)); if (IpAddress == BE32(INADDR_NONE)) { printerrorf("Fatal: %s is not a valid IPv4 address\n", szIpAddress); exit(VLMCSD_EINVAL); } char* next; Cidr = (uint8_t)strtol(szMask, &next, 10); if (*next || Cidr < 8 || Cidr > 30) { printerrorf("Fatal: /%s is not a valid CIDR mask between /8 and /30\n", szMask); exit(VLMCSD_EINVAL); } if (!((DhcpLeaseDuration = timeSpanString2Seconds(szLeaseDuration)))) { printerrorf("Fatal: No valid time span specified in option -%c.\n", 'O'); exit(VLMCSD_EINVAL); } Mask = (uint32_t)~(0xffffffff >> Cidr); Network = IpAddress & Mask; Broadcast = IpAddress | ~Mask; DhcpServer = IpAddress + 1; if (IpAddress <= Network || IpAddress + 1 >= Broadcast) { uint32_t lowerIpBE = BE32(Network + 1); uint32_t upperIpBE = BE32(Broadcast - 2); const char* szLower = vlmcsd_strdup(inet_ntoa(*(struct in_addr*)&lowerIpBE)); const char* szUpper = vlmcsd_strdup(inet_ntoa(*(struct in_addr*)&upperIpBE)); printerrorf("Fatal: For this subnet the IPv4 address must be "); if (lowerIpBE == upperIpBE) { printerrorf("%s\n", szLower); } else { printerrorf("between %s and %s\n", szLower, szUpper); } exit(VLMCSD_EINVAL); } } __noreturn static void WinErrorExit(DWORD error) { printerrorf("Registry read error: %s\n", win_strerror((int)error)); exit(error); } static HANDLE OpenTapHandle() { HANDLE handle = INVALID_HANDLE_VALUE; HKEY regAdapterKey; DWORD regResult; if ((regResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ | KEY_WOW64_64KEY, ®AdapterKey)) != ERROR_SUCCESS) { WinErrorExit(regResult); } char subkeyName[TAP_REGISTRY_DATA_SIZE]; DWORD i, subKeySize = sizeof(subkeyName); for (i = 0; (regResult = RegEnumKeyEx(regAdapterKey, i, subkeyName, &subKeySize, NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS; i++) { HKEY regSubKey; DWORD type, regDataSize; char regData[TAP_REGISTRY_DATA_SIZE]; if (regResult) WinErrorExit(regResult); if ((regResult = RegOpenKeyEx(regAdapterKey, subkeyName, 0, KEY_READ | KEY_WOW64_64KEY, ®SubKey)) == ERROR_SUCCESS) { regDataSize = sizeof(regData); if (RegQueryValueEx(regSubKey, "ComponentId", NULL, &type, (LPBYTE)regData, ®DataSize) == ERROR_SUCCESS) { if ( type == REG_SZ && ( !strncmp(regData, "tap0801", sizeof(regData)) || !strncmp(regData, "tap0901", sizeof(regData)) || !strncmp(regData, "TEAMVIEWERVPN", sizeof(regData)) ) ) { AdapterClass = vlmcsd_strdup(regData); regDataSize = sizeof(regData); if (RegQueryValueEx(regSubKey, "NetCfgInstanceId", NULL, &type, (LPBYTE)regData, ®DataSize) == ERROR_SUCCESS && type == REG_SZ) { HKEY connectionKey; char connectionKeyName[TAP_REGISTRY_DATA_SIZE]; strncpy(connectionKeyName, NETWORK_CONNECTIONS_KEY "\\", sizeof(connectionKeyName)); strncat(connectionKeyName, regData, sizeof(connectionKeyName)); strncat(connectionKeyName, "\\Connection", sizeof(connectionKeyName)); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, connectionKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &connectionKey) == ERROR_SUCCESS) { char deviceName[TAP_REGISTRY_DATA_SIZE]; regDataSize = sizeof(deviceName); if (RegQueryValueEx(connectionKey, "Name", NULL, &type, (LPBYTE)deviceName, ®DataSize) == ERROR_SUCCESS && type == REG_SZ) { if (!strcmp(szTapName, ".") || !strncasecmp(szTapName, deviceName, sizeof(deviceName))) { ActiveTapName = vlmcsd_strdup(deviceName); strncpy(deviceName, USERMODEDEVICEDIR, sizeof(deviceName)); strncat(deviceName, regData, sizeof(deviceName)); strncat(deviceName, strcmp(AdapterClass, "TEAMVIEWERVPN") ? TAP_WIN_SUFFIX : ".dgt", sizeof(deviceName)); handle = CreateFile(deviceName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL); } } } RegCloseKey(connectionKey); } if (handle == INVALID_HANDLE_VALUE) free(AdapterClass); } } } RegCloseKey(regSubKey); subKeySize = sizeof(subkeyName); if (handle != INVALID_HANDLE_VALUE) break; } RegCloseKey(regAdapterKey); if (handle == INVALID_HANDLE_VALUE) { printerrorf("Fatal: No compatible VPN adapter"); if (!strcmp(szTapName, ".")) { printerrorf("s"); } else { printerrorf(" with name \"%s\"", szTapName); } printerrorf(" available for use\n"); exit(ERROR_DEVICE_NOT_AVAILABLE); } return handle; } static int DevCtl(DWORD code, void* data, DWORD len) { if (!DeviceIoControl(TapHandle, code, data, len, data, len, &len, NULL)) { const DWORD error = GetLastError(); printerrorf("Fatal: VPN adapter error: %s\n", win_strerror(error)); exit(error); } return len; } static DWORD WINAPI TapMirror(LPVOID data_unused) { while (TRUE) { DWORD bytesRead, bytesWritten; if (!ReadFile(TapHandle, IpPacket, Mtu, &bytesRead, NULL)) break; const uint32_t temp = IpPacket->ip_src; IpPacket->ip_src = IpPacket->ip_dst; IpPacket->ip_dst = temp; if (!WriteFile(TapHandle, IpPacket, bytesRead, &bytesWritten, NULL)) break; # if !defined(NO_LOG) && defined(_PEDANTIC) if (bytesRead != bytesWritten) logger("Warning: VPN device \"%s\": %u bytes could not be written\n", ActiveTapName, bytesRead - bytesWritten); # endif // !defined(NO_LOG) && defined(_PEDANTIC) } const DWORD error = GetLastError(); # ifndef NO_LOG logger("Warning: VPN thread for device \"%s\" exiting: %s\n", ActiveTapName, win_strerror(error)); # endif // NO_LOG free(ActiveTapName); CloseHandle(TapHandle); exitOnWarningLevel(1); return error; } void startTap(char* const argument) { if (!strcmp(argument, "-")) return; parseTapArgument(argument); TapHandle = OpenTapHandle(); // Get MTU and driver version DevCtl(TAP_WIN_IOCTL_GET_MTU, &Mtu, sizeof(Mtu)); DevCtl(TAP_WIN_IOCTL_GET_VERSION, &DriverVersion, sizeof(DriverVersion)); // Configure TUN mode TapConfigTun_t tapTunCfg; tapTunCfg.Address.s_addr = BE32(IpAddress); tapTunCfg.Network.s_addr = BE32(Network); tapTunCfg.Mask.s_addr = BE32(Mask); DevCtl(TAP_WIN_IOCTL_CONFIG_TUN, &tapTunCfg, sizeof(tapTunCfg)); // Setup the drivers internal DHCP server TapConfigDhcp_t tapDhcpCfg; tapDhcpCfg.Address.s_addr = BE32(IpAddress); tapDhcpCfg.Mask.s_addr = BE32(Mask); tapDhcpCfg.DhcpServer.s_addr = BE32(IpAddress + 1); tapDhcpCfg.LeaseDuration = DhcpLeaseDuration; DevCtl(TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, &tapDhcpCfg, sizeof(tapDhcpCfg)); // Connect the virtual network cable BOOL isCableConnected = TRUE; DevCtl(TAP_WIN_IOCTL_SET_MEDIA_STATUS, &isCableConnected, sizeof(isCableConnected)); // Allocate buffer and start mirror thread IpPacket = (IpPacket_t*)vlmcsd_malloc(Mtu); HANDLE threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TapMirror, NULL, 0, NULL); if (!threadHandle) { DWORD error = GetLastError(); printerrorf("Fatal: Unable to start VPN thread: %s\n", win_strerror(error)); exit(error); } CloseHandle(threadHandle); # ifndef NO_LOG logger("%s %u.%u.%u device \"%s\" started\n", AdapterClass, DriverVersion.Major, DriverVersion.Minor, DriverVersion.Build, ActiveTapName); # endif // NO_LOG DWORD i; BOOL isAssigned; // Wait up to 4 seconds until the IP address is up and running // so vlmcsd can actually bind to and listen on it for (i = 0; !((isAssigned = isAddressAssigned())) && i < 20; i++) Sleep(200); if (!isAssigned) { printerrorf("Warning: IPv4 address %s not assigned\n", szIpAddress); } else { # ifndef NO_LOG logger("IPv4 address %s assigned\n", szIpAddress); # endif // NO_LOG } } #endif // NO_TAP