You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

326 lines
12 KiB

// tun_windows.c - Windows Wintun implementation
// Uses Wintun.dll for TUN device access
#include "tun_if.h"
#include "../lib/debug_config.h"
#include "../lib/wintun.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
// Wintun function pointers (wintun.h defines types as functions, need pointer syntax)
static HMODULE wintun_dll = NULL;
static WINTUN_CREATE_ADAPTER_FUNC *WintunCreateAdapter = NULL;
static WINTUN_CLOSE_ADAPTER_FUNC *WintunCloseAdapter = NULL;
static WINTUN_OPEN_ADAPTER_FUNC *WintunOpenAdapter = NULL;
static WINTUN_GET_ADAPTER_LUID_FUNC *WintunGetAdapterLUID = NULL;
static WINTUN_START_SESSION_FUNC *WintunStartSession = NULL;
static WINTUN_END_SESSION_FUNC *WintunEndSession = NULL;
static WINTUN_GET_READ_WAIT_EVENT_FUNC *WintunGetReadWaitEvent = NULL;
static WINTUN_RECEIVE_PACKET_FUNC *WintunReceivePacket = NULL;
static WINTUN_RELEASE_RECEIVE_PACKET_FUNC *WintunReleaseReceivePacket = NULL;
static WINTUN_ALLOCATE_SEND_PACKET_FUNC *WintunAllocateSendPacket = NULL;
static WINTUN_SEND_PACKET_FUNC *WintunSendPacket = NULL;
// Load Wintun.dll and resolve function pointers
static int wintun_load_dll(void) {
if (wintun_dll) {
return 0; // Already loaded
}
// Try to load from the same directory as executable
wchar_t path[MAX_PATH];
DWORD path_len = GetModuleFileNameW(NULL, path, MAX_PATH);
if (path_len > 0 && path_len < MAX_PATH) {
// Find last backslash
for (int i = path_len - 1; i >= 0; i--) {
if (path[i] == L'\\' || path[i] == L'/') {
path[i + 1] = L'\0';
break;
}
}
wcscat(path, L"wintun.dll");
wintun_dll = LoadLibraryW(path);
}
// If not found in executable directory, try system directories
if (!wintun_dll) {
wintun_dll = LoadLibraryA("wintun.dll");
}
if (!wintun_dll) {
// Try to construct full path for error message
wchar_t full_path[MAX_PATH];
GetCurrentDirectoryW(MAX_PATH, full_path);
wcscat(full_path, L"\\wintun.dll");
char path_utf8[MAX_PATH];
WideCharToMultiByte(CP_UTF8, 0, full_path, -1, path_utf8, MAX_PATH, NULL, NULL);
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to load wintun.dll from: %s", path_utf8);
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Please ensure wintun.dll is in the same directory as the executable");
fprintf(stderr, "FATAL ERROR: wintun.dll not found at: %s\n", path_utf8);
fprintf(stderr, "Please download Wintun from https://www.wintun.net/\n");
fprintf(stderr, "and place wintun.dll in the same directory as utun.exe\n");
return -1;
}
// Resolve function pointers (cast to void* first to avoid errors)
WintunCreateAdapter = (WINTUN_CREATE_ADAPTER_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunCreateAdapter");
WintunCloseAdapter = (WINTUN_CLOSE_ADAPTER_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunCloseAdapter");
WintunOpenAdapter = (WINTUN_OPEN_ADAPTER_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunOpenAdapter");
WintunGetAdapterLUID = (WINTUN_GET_ADAPTER_LUID_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunGetAdapterLUID");
WintunStartSession = (WINTUN_START_SESSION_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunStartSession");
WintunEndSession = (WINTUN_END_SESSION_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunEndSession");
WintunGetReadWaitEvent = (WINTUN_GET_READ_WAIT_EVENT_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunGetReadWaitEvent");
WintunReceivePacket = (WINTUN_RECEIVE_PACKET_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunReceivePacket");
WintunReleaseReceivePacket = (WINTUN_RELEASE_RECEIVE_PACKET_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunReleaseReceivePacket");
WintunAllocateSendPacket = (WINTUN_ALLOCATE_SEND_PACKET_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunAllocateSendPacket");
WintunSendPacket = (WINTUN_SEND_PACKET_FUNC *)(void *)GetProcAddress(wintun_dll, "WintunSendPacket");
// Check that all functions were resolved
if (!WintunCreateAdapter || !WintunCloseAdapter || !WintunOpenAdapter ||
!WintunGetAdapterLUID || !WintunStartSession || !WintunEndSession ||
!WintunGetReadWaitEvent || !WintunReceivePacket || !WintunReleaseReceivePacket ||
!WintunAllocateSendPacket || !WintunSendPacket) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to resolve some Wintun functions");
FreeLibrary(wintun_dll);
wintun_dll = NULL;
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Wintun.dll loaded successfully");
return 0;
}
// Unload Wintun.dll
static void wintun_unload_dll(void) {
if (wintun_dll) {
FreeLibrary(wintun_dll);
wintun_dll = NULL;
WintunCreateAdapter = NULL;
WintunCloseAdapter = NULL;
WintunOpenAdapter = NULL;
WintunGetAdapterLUID = NULL;
WintunStartSession = NULL;
WintunEndSession = NULL;
WintunGetReadWaitEvent = NULL;
WintunReceivePacket = NULL;
WintunReleaseReceivePacket = NULL;
WintunAllocateSendPacket = NULL;
WintunSendPacket = NULL;
}
}
// Set IP address and MTU using IP Helper API
static int wintun_set_ip_and_mtu(const NET_LUID* luid, const char* ip_str, int mtu) {
// Parse IP address (remove /32 suffix if present)
char ip_copy[64];
strncpy(ip_copy, ip_str, sizeof(ip_copy) - 1);
ip_copy[sizeof(ip_copy) - 1] = '\0';
char* slash = strchr(ip_copy, '/');
if (slash) *slash = '\0';
// Convert IP string to sockaddr
SOCKADDR_INET addr;
memset(&addr, 0, sizeof(addr));
addr.si_family = AF_INET;
if (inet_pton(AF_INET, ip_copy, &addr.Ipv4.sin_addr) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to parse IP address: %s", ip_copy);
return -1;
}
// Get interface index from LUID
NET_IFINDEX ifindex;
if (ConvertInterfaceLuidToIndex(luid, &ifindex) != NO_ERROR) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to convert LUID to interface index");
return -1;
}
// Set MTU first using MIB_IPINTERFACE_ROW (minimal required fields)
MIB_IPINTERFACE_ROW row;
memset(&row, 0, sizeof(row));
row.Family = AF_INET;
row.InterfaceLuid = *luid;
row.NlMtu = mtu;
DWORD ret = SetIpInterfaceEntry(&row);
if (ret != NO_ERROR && ret != ERROR_OBJECT_ALREADY_EXISTS) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Failed to set MTU: %lu", ret);
}
// Add unicast IP address
MIB_UNICASTIPADDRESS_ROW addr_row;
InitializeUnicastIpAddressEntry(&addr_row);
addr_row.InterfaceLuid = *luid;
addr_row.Address = addr;
addr_row.OnLinkPrefixLength = 32; // /32 for point-to-point
addr_row.ValidLifetime = 0xffffffff;
addr_row.PreferredLifetime = 0xffffffff;
ret = CreateUnicastIpAddressEntry(&addr_row);
if (ret != NO_ERROR) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set IP address: %lu", ret);
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "IP address %s and MTU %d configured successfully", ip_copy, mtu);
return 0;
}
// Platform-specific initialization for Windows
int tun_platform_init(struct tun_if* tun, const char* ifname, const char* ip_str, int mtu) {
// Load Wintun.dll
if (wintun_load_dll() != 0) {
return -1;
}
// Generate adapter name
const WCHAR* adapter_name = L"utun";
const WCHAR* tunnel_type = L"utun";
// Use provided name or default
wchar_t wname[128];
if (ifname && ifname[0] != '\0') {
MultiByteToWideChar(CP_UTF8, 0, ifname, -1, wname, 128);
adapter_name = wname;
}
// Create or open Wintun adapter
GUID guid = {0};
WINTUN_ADAPTER_HANDLE adapter = WintunCreateAdapter(adapter_name, tunnel_type, &guid);
if (!adapter) {
DWORD err = GetLastError();
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create Wintun adapter: %lu", err);
return -1;
}
// Get adapter LUID for IP configuration
NET_LUID luid;
WintunGetAdapterLUID(adapter, &luid);
// Start Wintun session
WINTUN_SESSION_HANDLE session = WintunStartSession(adapter, WINTUN_MIN_RING_CAPACITY);
if (!session) {
DWORD err = GetLastError();
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to start Wintun session: %lu", err);
WintunCloseAdapter(adapter);
return -1;
}
// Store handles
tun->adapter_handle = adapter;
tun->platform_handle = session;
// Set IP and MTU
if (wintun_set_ip_and_mtu(&luid, ip_str, mtu) != 0) {
WintunEndSession(session);
WintunCloseAdapter(adapter);
tun->adapter_handle = NULL;
tun->platform_handle = NULL;
return -1;
}
// Store interface name
strncpy(tun->ifname, ifname && ifname[0] ? ifname : "utun", sizeof(tun->ifname) - 1);
tun->ifname[sizeof(tun->ifname) - 1] = '\0';
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Wintun adapter created: %s", tun->ifname);
return 0;
}
// Platform-specific cleanup for Windows
void tun_platform_cleanup(struct tun_if* tun) {
if (tun->platform_handle) {
WintunEndSession((WINTUN_SESSION_HANDLE)tun->platform_handle);
tun->platform_handle = NULL;
}
if (tun->adapter_handle) {
WintunCloseAdapter((WINTUN_ADAPTER_HANDLE)tun->adapter_handle);
tun->adapter_handle = NULL;
}
}
// Platform-specific read for Windows
ssize_t tun_platform_read(struct tun_if* tun, uint8_t* buf, size_t len) {
(void)len; // Wintun handles packet size
if (!tun->platform_handle) {
return -1;
}
DWORD packet_size;
BYTE* packet = WintunReceivePacket((WINTUN_SESSION_HANDLE)tun->platform_handle, &packet_size);
if (!packet) {
DWORD err = GetLastError();
if (err == ERROR_NO_MORE_ITEMS) {
return 0; // No data available (non-blocking)
}
if (err == ERROR_HANDLE_EOF) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Wintun adapter closed");
return -1;
}
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Wintun receive error: %lu", err);
return -1;
}
// Copy packet data
if (packet_size > len) {
packet_size = (DWORD)len; // Truncate if buffer too small
}
memcpy(buf, packet, packet_size);
// Release packet back to Wintun
WintunReleaseReceivePacket((WINTUN_SESSION_HANDLE)tun->platform_handle, packet);
return (ssize_t)packet_size;
}
// Platform-specific write for Windows
ssize_t tun_platform_write(struct tun_if* tun, const uint8_t* buf, size_t len) {
if (!tun->platform_handle) {
return -1;
}
// Allocate packet from Wintun ring
BYTE* packet = WintunAllocateSendPacket((WINTUN_SESSION_HANDLE)tun->platform_handle, (DWORD)len);
if (!packet) {
DWORD err = GetLastError();
if (err == ERROR_HANDLE_EOF) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Wintun adapter closed");
return -1;
}
if (err == ERROR_BUFFER_OVERFLOW) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Wintun send buffer overflow");
return -1;
}
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Wintun allocate packet failed: %lu", err);
return -1;
}
// Copy data and send
memcpy(packet, buf, len);
WintunSendPacket((WINTUN_SESSION_HANDLE)tun->platform_handle, packet);
return (ssize_t)len;
}
// Get poll fd for uasync
int tun_platform_get_poll_fd(struct tun_if* tun) {
if (!tun->platform_handle) {
return -1;
}
HANDLE event = WintunGetReadWaitEvent((WINTUN_SESSION_HANDLE)tun->platform_handle);
if (event == NULL || event == INVALID_HANDLE_VALUE) {
return -1;
}
// Cast HANDLE to int for uasync (Windows specific)
return (int)(intptr_t)event;
}