├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs └── src ├── cpp ├── simple_wintun.cpp ├── simple_wintun.h └── wintun.h └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_wintun" 3 | version = "0.1.0" 4 | authors = ["xutianyi <853122726@qq.com>"] 5 | edition = "2021" 6 | description = "Wintun rust API library" 7 | license = "MIT OR Apache-2.0" 8 | 9 | [build-dependencies] 10 | cc = "1" 11 | 12 | [dependencies] 13 | once_cell = "1" 14 | log = "0.4" 15 | blocking = { version = "1", optional = true } 16 | 17 | [features] 18 | async = ["blocking"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleWintun 2 | 3 | Wintun rust API library 4 | 5 | ### Usage 6 | 7 | wintun.dll (https://www.wintun.net) needs to be in the same directory as the executable file or in System32 8 | 9 | Cargo.toml 10 | 11 | ```toml 12 | [dependencies] 13 | simple_wintun = { git = "https://github.com/xutianyi1999/SimpleWintun.git" } 14 | ``` 15 | 16 | main.rs 17 | 18 | ```rust 19 | use std::net::Ipv4Addr; 20 | use std::ops::Range; 21 | use simple_wintun::adapter::WintunAdapter; 22 | 23 | const SRC_ADDR: Range = 12..16; 24 | const DST_ADDR: Range = 16..20; 25 | 26 | fn main() { 27 | let adapter = WintunAdapter::create_adapter("example", "test", "{D4C24D32-A723-DB80-A493-4E32E7883F15}").unwrap(); 28 | adapter.set_ipaddr("192.168.8.1", 24).unwrap(); 29 | let session = adapter.start_session(0x20000).unwrap(); 30 | 31 | let mut buff = vec![0u8; 65536]; 32 | 33 | loop { 34 | let packet = match session.read_packet(&mut buff) { 35 | Ok(len) => &buff[..len], 36 | Err(e) => { 37 | eprintln!("error: {}", e); 38 | return 39 | } 40 | }; 41 | 42 | let mut addr_buff = [0u8; 4]; 43 | 44 | addr_buff.copy_from_slice(&packet[SRC_ADDR]); 45 | let src_addr = Ipv4Addr::from(addr_buff); 46 | 47 | addr_buff.copy_from_slice(&packet[DST_ADDR]); 48 | let dst_addr = Ipv4Addr::from(addr_buff); 49 | 50 | println!("packet {} -> {}", src_addr, dst_addr) 51 | } 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | cc::Build::new() 3 | .cpp(true) 4 | .include("src/cpp") 5 | .file("src/cpp/simple_wintun.cpp") 6 | .compile("SimpleWintunAPI"); 7 | } -------------------------------------------------------------------------------- /src/cpp/simple_wintun.cpp: -------------------------------------------------------------------------------- 1 | #include "simple_wintun.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #pragma comment(lib, "iphlpapi") 7 | #pragma comment(lib, "ole32") 8 | 9 | static WINTUN_CREATE_ADAPTER_FUNC *WintunCreateAdapter; 10 | static WINTUN_CLOSE_ADAPTER_FUNC *WintunCloseAdapter; 11 | static WINTUN_OPEN_ADAPTER_FUNC *WintunOpenAdapter; 12 | static WINTUN_GET_ADAPTER_LUID_FUNC *WintunGetAdapterLUID; 13 | static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC *WintunGetRunningDriverVersion; 14 | static WINTUN_DELETE_DRIVER_FUNC *WintunDeleteDriver; 15 | static WINTUN_SET_LOGGER_FUNC *WintunSetLogger; 16 | static WINTUN_START_SESSION_FUNC *WintunStartSession; 17 | static WINTUN_END_SESSION_FUNC *WintunEndSession; 18 | static WINTUN_GET_READ_WAIT_EVENT_FUNC *WintunGetReadWaitEvent; 19 | static WINTUN_RECEIVE_PACKET_FUNC *WintunReceivePacket; 20 | static WINTUN_RELEASE_RECEIVE_PACKET_FUNC *WintunReleaseReceivePacket; 21 | static WINTUN_ALLOCATE_SEND_PACKET_FUNC *WintunAllocateSendPacket; 22 | static WINTUN_SEND_PACKET_FUNC *WintunSendPacket; 23 | 24 | std::wstring get_ws(const char *c) { 25 | std::string str = c; 26 | std::wstring wstr = std::wstring(str.begin(), str.end()); 27 | return wstr; 28 | } 29 | 30 | static HMODULE 31 | initialize() { 32 | HMODULE Wintun = LoadLibraryExW(L"wintun.dll", nullptr, 33 | LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); 34 | if (!Wintun) 35 | return nullptr; 36 | #define X(Name) ((*(FARPROC *)&Name = GetProcAddress(Wintun, #Name)) == NULL) 37 | if (X(WintunCreateAdapter) || X(WintunCloseAdapter) || X(WintunOpenAdapter) || X(WintunGetAdapterLUID) || 38 | X(WintunGetRunningDriverVersion) || X(WintunDeleteDriver) || X(WintunSetLogger) || X(WintunStartSession) || 39 | X(WintunEndSession) || X(WintunGetReadWaitEvent) || X(WintunReceivePacket) || X(WintunReleaseReceivePacket) || 40 | X(WintunAllocateSendPacket) || X(WintunSendPacket)) 41 | #undef X 42 | { 43 | DWORD LastError = GetLastError(); 44 | FreeLibrary(Wintun); 45 | SetLastError(LastError); 46 | return NULL; 47 | } 48 | return Wintun; 49 | } 50 | 51 | CODE initialize_wintun() { 52 | HMODULE wintun = initialize(); 53 | 54 | if (wintun) { 55 | return SUCCESS_CODE; 56 | } else { 57 | return OS_ERROR_CODE; 58 | } 59 | } 60 | 61 | CODE delete_driver() { 62 | BOOL res = WintunDeleteDriver(); 63 | 64 | if (res == 0) { 65 | return OS_ERROR_CODE; 66 | } 67 | return SUCCESS_CODE; 68 | } 69 | 70 | CODE create_adapter( 71 | const char *adapter_name, 72 | const char *tunnel_type, 73 | const char *guid_str, 74 | WINTUN_ADAPTER_HANDLE *adapter 75 | ) { 76 | auto wc_adapter_name = get_ws(adapter_name); 77 | auto wc_tunnel_type = get_ws(tunnel_type); 78 | auto wc_guid = get_ws(guid_str); 79 | const wchar_t *pguid = wc_guid.c_str(); 80 | 81 | GUID guid; 82 | auto res = CLSIDFromString(pguid, (LPCLSID) &guid); 83 | 84 | if (res != S_OK) { 85 | return PARSE_GUID_ERROR_CODE; 86 | } 87 | 88 | WINTUN_ADAPTER_HANDLE inner_adapter = WintunCreateAdapter( 89 | wc_adapter_name.c_str(), 90 | wc_tunnel_type.c_str(), 91 | &guid 92 | ); 93 | 94 | if (!inner_adapter) { 95 | return OS_ERROR_CODE; 96 | } 97 | 98 | *adapter = inner_adapter; 99 | return SUCCESS_CODE; 100 | } 101 | 102 | CODE open_adapter(const char *adapter_name, WINTUN_ADAPTER_HANDLE *adapter) { 103 | auto wc_adapter_name = get_ws(adapter_name); 104 | auto inner_adapter = WintunOpenAdapter(wc_adapter_name.c_str()); 105 | 106 | if (inner_adapter) { 107 | *adapter = inner_adapter; 108 | return SUCCESS_CODE; 109 | } else { 110 | return OS_ERROR_CODE; 111 | } 112 | } 113 | 114 | void close_adapter(WINTUN_ADAPTER_HANDLE adapter) { 115 | WintunCloseAdapter(adapter); 116 | } 117 | 118 | NET_LUID get_adapter_luid(WINTUN_ADAPTER_HANDLE adapter) { 119 | NET_LUID luid = NET_LUID{}; 120 | WintunGetAdapterLUID(adapter, &luid); 121 | return luid; 122 | } 123 | 124 | CODE get_driver_version(unsigned long *version) { 125 | *version = WintunGetRunningDriverVersion(); 126 | 127 | if (*version == 0) { 128 | return OS_ERROR_CODE; 129 | } 130 | return SUCCESS_CODE; 131 | } 132 | 133 | CODE start_session( 134 | WINTUN_ADAPTER_HANDLE adapter, 135 | unsigned long capacity, 136 | WINTUN_SESSION_HANDLE *session 137 | ) { 138 | WINTUN_SESSION_HANDLE inner_session = WintunStartSession(adapter, capacity); 139 | 140 | if (!inner_session) { 141 | return OS_ERROR_CODE; 142 | } 143 | 144 | *session = inner_session; 145 | return SUCCESS_CODE; 146 | } 147 | 148 | void end_session(WINTUN_SESSION_HANDLE session) { 149 | WintunEndSession(session); 150 | } 151 | 152 | EVENT get_read_wait_event(WINTUN_SESSION_HANDLE session) { 153 | return WintunGetReadWaitEvent(session); 154 | } 155 | 156 | CODE wait_event(EVENT read_wait, unsigned int time_ms) { 157 | auto res = WaitForSingleObject(read_wait, time_ms); 158 | 159 | if (res == WAIT_OBJECT_0) { 160 | return SUCCESS_CODE; 161 | } else if (res == WAIT_TIMEOUT) { 162 | return EVENT_WAIT_TIMEOUT; 163 | } else { 164 | return OS_ERROR_CODE; 165 | } 166 | } 167 | 168 | CODE read_packet_nonblock( 169 | WINTUN_SESSION_HANDLE session, 170 | BYTE *buff, 171 | unsigned long *size 172 | ) { 173 | unsigned long packet_size; 174 | BYTE *packet = WintunReceivePacket(session, &packet_size); 175 | 176 | if (packet) { 177 | if (*size >= packet_size) { 178 | memcpy(buff, packet, packet_size); 179 | *size = packet_size; 180 | WintunReleaseReceivePacket(session, packet); 181 | 182 | return SUCCESS_CODE; 183 | } else { 184 | WintunReleaseReceivePacket(session, packet); 185 | *size = packet_size; 186 | return NOT_ENOUGH_SIZE_CODE; 187 | } 188 | } else { 189 | return OS_ERROR_CODE; 190 | } 191 | } 192 | 193 | CODE read_packet( 194 | WINTUN_SESSION_HANDLE session, 195 | EVENT read_wait, 196 | BYTE *buff, 197 | unsigned long *size 198 | ) { 199 | while (true) { 200 | CODE res = read_packet_nonblock(session, buff, size); 201 | 202 | if (res != OS_ERROR_CODE) { 203 | return res; 204 | } 205 | 206 | DWORD last_error = GetLastError(); 207 | 208 | if (last_error == ERROR_NO_MORE_ITEMS) { 209 | if (WaitForSingleObject(read_wait, INFINITE) != WAIT_OBJECT_0) { 210 | return OS_ERROR_CODE; 211 | } 212 | } else { 213 | return OS_ERROR_CODE; 214 | } 215 | } 216 | } 217 | 218 | CODE write_packet( 219 | WINTUN_SESSION_HANDLE session, 220 | BYTE *buff, 221 | unsigned long size 222 | ) { 223 | BYTE *data = WintunAllocateSendPacket(session, size); 224 | 225 | if (data) { 226 | memcpy(data, buff, size); 227 | WintunSendPacket(session, data); 228 | return SUCCESS_CODE; 229 | } else { 230 | return OS_ERROR_CODE; 231 | } 232 | } 233 | 234 | CODE set_ipaddr( 235 | WINTUN_ADAPTER_HANDLE adapter, 236 | const char *ipaddr, 237 | BYTE subnet_mask 238 | ) { 239 | MIB_UNICASTIPADDRESS_ROW AddressRow; 240 | InitializeUnicastIpAddressEntry(&AddressRow); 241 | WintunGetAdapterLUID(adapter, &AddressRow.InterfaceLuid); 242 | AddressRow.Address.Ipv4.sin_family = AF_INET; 243 | AddressRow.OnLinkPrefixLength = subnet_mask; 244 | auto res = inet_pton(AF_INET, ipaddr, &(AddressRow.Address.Ipv4.sin_addr)); 245 | 246 | if (res != 1) { 247 | return IP_ADDRESS_ERROR_CODE; 248 | } 249 | 250 | auto res2 = CreateUnicastIpAddressEntry(&AddressRow); 251 | 252 | if (res2 != ERROR_SUCCESS && res2 != ERROR_OBJECT_ALREADY_EXISTS) { 253 | return IP_ADDRESS_ERROR_CODE; 254 | } 255 | return SUCCESS_CODE; 256 | } -------------------------------------------------------------------------------- /src/cpp/simple_wintun.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wintun.h" 4 | 5 | typedef void *EVENT; 6 | typedef unsigned char CODE; 7 | typedef unsigned char BYTE; 8 | 9 | const CODE SUCCESS_CODE = 0; 10 | const CODE OS_ERROR_CODE = 1; 11 | const CODE NOT_ENOUGH_SIZE_CODE = 2; 12 | const CODE PARSE_GUID_ERROR_CODE = 3; 13 | const CODE IP_ADDRESS_ERROR_CODE = 4; 14 | const CODE EVENT_WAIT_TIMEOUT = 5; 15 | 16 | extern "C" { 17 | CODE initialize_wintun(); 18 | 19 | CODE delete_driver(); 20 | 21 | CODE create_adapter( 22 | const char *adapter_name, 23 | const char *tunnel_type, 24 | const char *guid_str, 25 | WINTUN_ADAPTER_HANDLE *adapter 26 | ); 27 | 28 | CODE open_adapter(const char *adapter_name, WINTUN_ADAPTER_HANDLE *adapter); 29 | 30 | void close_adapter(WINTUN_ADAPTER_HANDLE adapter); 31 | 32 | NET_LUID get_adapter_luid(WINTUN_ADAPTER_HANDLE adapter); 33 | 34 | CODE get_driver_version(unsigned long *version); 35 | 36 | CODE start_session( 37 | WINTUN_ADAPTER_HANDLE adapter, 38 | unsigned long capacity, 39 | WINTUN_SESSION_HANDLE *session 40 | ); 41 | 42 | void end_session(WINTUN_SESSION_HANDLE session); 43 | 44 | EVENT get_read_wait_event(WINTUN_SESSION_HANDLE session); 45 | 46 | CODE wait_event(EVENT read_wait, unsigned int time_ms); 47 | 48 | CODE read_packet_nonblock( 49 | WINTUN_SESSION_HANDLE session, 50 | BYTE *buff, 51 | unsigned long *size 52 | ); 53 | 54 | CODE read_packet( 55 | WINTUN_SESSION_HANDLE session, 56 | EVENT read_wait, 57 | BYTE *buff, 58 | unsigned long *size 59 | ); 60 | 61 | CODE write_packet( 62 | WINTUN_SESSION_HANDLE session, 63 | BYTE *buff, 64 | unsigned long size 65 | ); 66 | 67 | CODE set_ipaddr( 68 | WINTUN_ADAPTER_HANDLE adapter, 69 | const char *ipaddr, 70 | BYTE subnet_mask 71 | ); 72 | } -------------------------------------------------------------------------------- /src/cpp/wintun.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT 2 | * 3 | * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #ifndef ALIGNED 19 | # if defined(_MSC_VER) 20 | # define ALIGNED(n) __declspec(align(n)) 21 | # elif defined(__GNUC__) 22 | # define ALIGNED(n) __attribute__((aligned(n))) 23 | # else 24 | # error "Unable to define ALIGNED" 25 | # endif 26 | #endif 27 | 28 | /* MinGW is missing this one, unfortunately. */ 29 | #ifndef _Post_maybenull_ 30 | # define _Post_maybenull_ 31 | #endif 32 | 33 | #pragma warning(push) 34 | #pragma warning(disable : 4324) /* structure was padded due to alignment specifier */ 35 | 36 | /** 37 | * A handle representing Wintun adapter 38 | */ 39 | typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE; 40 | 41 | /** 42 | * Creates a new Wintun adapter. 43 | * 44 | * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 45 | * characters. 46 | * 47 | * @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1 48 | * characters. 49 | * 50 | * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically. 51 | * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is 52 | * created for each new adapter. It is called "requested" GUID because the API it uses is 53 | * completely undocumented, and so there could be minor interesting complications with its usage. 54 | * 55 | * @return If the function succeeds, the return value is the adapter handle. Must be released with 56 | * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call 57 | * GetLastError. 58 | */ 59 | typedef _Must_inspect_result_ 60 | _Return_type_success_(return != NULL) 61 | _Post_maybenull_ 62 | WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC) 63 | (_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID); 64 | 65 | /** 66 | * Opens an existing Wintun adapter. 67 | * 68 | * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 69 | * characters. 70 | * 71 | * @return If the function succeeds, the return value is the adapter handle. Must be released with 72 | * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call 73 | * GetLastError. 74 | */ 75 | typedef _Must_inspect_result_ 76 | _Return_type_success_(return != NULL) 77 | _Post_maybenull_ 78 | WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name); 79 | 80 | /** 81 | * Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. 82 | * 83 | * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. 84 | */ 85 | typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter); 86 | 87 | /** 88 | * Deletes the Wintun driver if there are no more adapters in use. 89 | * 90 | * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To 91 | * get extended error information, call GetLastError. 92 | */ 93 | typedef _Return_type_success_(return != FALSE) 94 | BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID); 95 | 96 | /** 97 | * Returns the LUID of the adapter. 98 | * 99 | * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter 100 | * 101 | * @param Luid Pointer to LUID to receive adapter LUID. 102 | */ 103 | typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid); 104 | 105 | /** 106 | * Determines the version of the Wintun driver currently loaded. 107 | * 108 | * @return If the function succeeds, the return value is the version number. If the function fails, the return value is 109 | * zero. To get extended error information, call GetLastError. Possible errors include the following: 110 | * ERROR_FILE_NOT_FOUND Wintun not loaded 111 | */ 112 | typedef _Return_type_success_(return != 0) 113 | DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID); 114 | 115 | /** 116 | * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK. 117 | */ 118 | typedef enum { 119 | WINTUN_LOG_INFO, /**< Informational */ 120 | WINTUN_LOG_WARN, /**< Warning */ 121 | WINTUN_LOG_ERR /**< Error */ 122 | } WINTUN_LOGGER_LEVEL; 123 | 124 | /** 125 | * Called by internal logger to report diagnostic messages 126 | * 127 | * @param Level Message level. 128 | * 129 | * @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC. 130 | * 131 | * @param Message Message text. 132 | */ 133 | typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)( 134 | _In_ WINTUN_LOGGER_LEVEL Level, 135 | _In_ DWORD64 Timestamp, 136 | _In_z_ LPCWSTR Message); 137 | 138 | /** 139 | * Sets logger callback function. 140 | * 141 | * @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various 142 | * threads concurrently. Should the logging require serialization, you must handle serialization in 143 | * NewLogger. Set to NULL to disable. 144 | */ 145 | typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger); 146 | 147 | /** 148 | * Minimum ring capacity. 149 | */ 150 | #define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */ 151 | 152 | /** 153 | * Maximum ring capacity. 154 | */ 155 | #define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */ 156 | 157 | /** 158 | * A handle representing Wintun session 159 | */ 160 | typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE; 161 | 162 | /** 163 | * Starts Wintun session. 164 | * 165 | * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter 166 | * 167 | * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.) 168 | * Must be a power of two. 169 | * 170 | * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is 171 | * NULL. To get extended error information, call GetLastError. 172 | */ 173 | typedef _Must_inspect_result_ 174 | _Return_type_success_(return != NULL) 175 | _Post_maybenull_ 176 | WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity); 177 | 178 | /** 179 | * Ends Wintun session. 180 | * 181 | * @param Session Wintun session handle obtained with WintunStartSession 182 | */ 183 | typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); 184 | 185 | /** 186 | * Gets Wintun session's read-wait event handle. 187 | * 188 | * @param Session Wintun session handle obtained with WintunStartSession 189 | * 190 | * @return Pointer to receive event handle to wait for available data when reading. Should 191 | * WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy 192 | * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call 193 | * CloseHandle on this event - it is managed by the session. 194 | */ 195 | typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); 196 | 197 | /** 198 | * Maximum IP packet size 199 | */ 200 | #define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF 201 | 202 | /** 203 | * Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned 204 | * from this function to release internal buffer. This function is thread-safe. 205 | * 206 | * @param Session Wintun session handle obtained with WintunStartSession 207 | * 208 | * @param PacketSize Pointer to receive packet size. 209 | * 210 | * @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the 211 | * return value is NULL. To get extended error information, call GetLastError. Possible errors include the 212 | * following: 213 | * ERROR_HANDLE_EOF Wintun adapter is terminating; 214 | * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted; 215 | * ERROR_INVALID_DATA Wintun buffer is corrupt 216 | */ 217 | typedef _Must_inspect_result_ 218 | _Return_type_success_(return != NULL) 219 | _Post_maybenull_ 220 | _Post_writable_byte_size_(*PacketSize) 221 | BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize); 222 | 223 | /** 224 | * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe. 225 | * 226 | * @param Session Wintun session handle obtained with WintunStartSession 227 | * 228 | * @param Packet Packet obtained with WintunReceivePacket 229 | */ 230 | typedef VOID( 231 | WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); 232 | 233 | /** 234 | * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send 235 | * and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of 236 | * calls define the packet sending order. 237 | * 238 | * @param Session Wintun session handle obtained with WintunStartSession 239 | * 240 | * @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE. 241 | * 242 | * @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, 243 | * the return value is NULL. To get extended error information, call GetLastError. Possible errors include the 244 | * following: 245 | * ERROR_HANDLE_EOF Wintun adapter is terminating; 246 | * ERROR_BUFFER_OVERFLOW Wintun buffer is full; 247 | */ 248 | typedef _Must_inspect_result_ 249 | _Return_type_success_(return != NULL) 250 | _Post_maybenull_ 251 | _Post_writable_byte_size_(PacketSize) 252 | BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize); 253 | 254 | /** 255 | * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket 256 | * order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the 257 | * WintunSendPacket yet. 258 | * 259 | * @param Session Wintun session handle obtained with WintunStartSession 260 | * 261 | * @param Packet Packet obtained with WintunAllocateSendPacket 262 | */ 263 | typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); 264 | 265 | #pragma warning(pop) 266 | 267 | #ifdef __cplusplus 268 | } 269 | #endif 270 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use std::error::Error; 5 | use std::fmt::{Debug, Display, Formatter}; 6 | use std::io; 7 | use std::os::raw::c_void; 8 | 9 | pub type AdapterHandle = *mut c_void; 10 | pub type Session = *mut c_void; 11 | pub type Event = *mut c_void; 12 | pub type Code = u8; 13 | pub type LUID = u64; 14 | 15 | #[derive(Copy, Clone)] 16 | struct HandleWrap { 17 | handle: H, 18 | } 19 | 20 | impl HandleWrap { 21 | fn new(handle: H) -> Self { 22 | HandleWrap { handle } 23 | } 24 | } 25 | 26 | unsafe impl Send for HandleWrap {} 27 | 28 | unsafe impl Sync for HandleWrap {} 29 | 30 | mod ffi { 31 | use std::os::raw::c_char; 32 | 33 | use crate::{AdapterHandle, Code, Event, LUID, Session}; 34 | 35 | extern "C" { 36 | pub fn initialize_wintun() -> Code; 37 | 38 | pub fn delete_driver() -> Code; 39 | 40 | pub fn create_adapter( 41 | adapter_name: *const c_char, 42 | tunnel_type: *const c_char, 43 | guid_str: *const c_char, 44 | adapter: *mut AdapterHandle, 45 | ) -> Code; 46 | 47 | pub fn open_adapter( 48 | adapter_name: *const c_char, 49 | adapter: *mut AdapterHandle, 50 | ) -> Code; 51 | 52 | pub fn close_adapter(adapter: AdapterHandle); 53 | 54 | pub fn get_driver_version(version: *mut u32) -> Code; 55 | 56 | pub fn start_session( 57 | adapter: AdapterHandle, 58 | capacity: u32, 59 | session: *mut Session, 60 | ) -> Code; 61 | 62 | pub fn end_session(session: Session); 63 | 64 | pub fn get_read_wait_event(session: Session) -> Event; 65 | 66 | pub fn wait_event(event: Event, time: u32) -> Code; 67 | 68 | pub fn read_packet_nonblock( 69 | session: Session, 70 | buff: *mut u8, 71 | size: *mut u32, 72 | ) -> Code; 73 | 74 | pub fn read_packet( 75 | session: Session, 76 | read_wait: Event, 77 | buff: *mut u8, 78 | size: *mut u32, 79 | ) -> Code; 80 | 81 | pub fn write_packet( 82 | session: Session, 83 | buff: *const u8, 84 | size: u32, 85 | ) -> Code; 86 | 87 | pub fn set_ipaddr( 88 | adapter: AdapterHandle, 89 | ipaddr: *const c_char, 90 | subnet_mask: u8, 91 | ) -> Code; 92 | 93 | pub fn get_adapter_luid(adapter: AdapterHandle) -> LUID; 94 | } 95 | } 96 | 97 | #[derive(Debug)] 98 | pub enum WaitError { 99 | Timeout, 100 | IoError(io::Error), 101 | } 102 | 103 | impl Display for WaitError { 104 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 105 | match self { 106 | WaitError::Timeout => write!(f, "Wait timeout"), 107 | WaitError::IoError(e) => write!(f, "{}", e) 108 | } 109 | } 110 | } 111 | 112 | impl Error for WaitError {} 113 | 114 | #[derive(Debug)] 115 | pub enum ReadError { 116 | NotEnoughSize(usize), 117 | IoError(io::Error), 118 | NoMoreItems, 119 | } 120 | 121 | impl Display for ReadError { 122 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 123 | match self { 124 | ReadError::IoError(e) => write!(f, "{}", e), 125 | ReadError::NotEnoughSize(len) => write!(f, "Not enough size: {}", len), 126 | ReadError::NoMoreItems => write!(f, "No more items") 127 | } 128 | } 129 | } 130 | 131 | impl Error for ReadError {} 132 | 133 | impl From for io::Error { 134 | fn from(e: ReadError) -> Self { 135 | match e { 136 | ReadError::NotEnoughSize(_) => io::Error::new(io::ErrorKind::Other, e), 137 | ReadError::IoError(e) => e, 138 | ReadError::NoMoreItems => io::Error::new(io::ErrorKind::WouldBlock, "No more items") 139 | } 140 | } 141 | } 142 | 143 | pub mod raw { 144 | use std::io::{Error, ErrorKind, Result}; 145 | use std::os::raw::c_char; 146 | use std::ptr::null_mut; 147 | 148 | use crate::{AdapterHandle, Code, Event, ffi, LUID, ReadError, Session, WaitError}; 149 | 150 | const SUCCESS_CODE: Code = 0; 151 | #[allow(dead_code)] 152 | const OS_ERROR_CODE: Code = 1; 153 | const NOT_ENOUGH_SIZE_CODE: Code = 2; 154 | const PARSE_GUID_ERROR_CODE: Code = 3; 155 | #[allow(dead_code)] 156 | const IP_ADDRESS_ERROR_CODE: Code = 4; 157 | const EVENT_WAIT_TIMEOUT: Code = 5; 158 | 159 | pub fn initialize() -> Result<()> { 160 | let res = unsafe { ffi::initialize_wintun() }; 161 | 162 | match res { 163 | SUCCESS_CODE => Ok(()), 164 | _ => Err(Error::last_os_error()) 165 | } 166 | } 167 | 168 | pub fn delete_driver() -> Result<()> { 169 | let res = unsafe { ffi::delete_driver() }; 170 | 171 | match res { 172 | SUCCESS_CODE => Ok(()), 173 | _ => Err(Error::last_os_error()) 174 | } 175 | } 176 | 177 | pub fn create_adapter( 178 | adapter_name: &str, 179 | tunnel_type: &str, 180 | guid: &str, 181 | ) -> Result { 182 | let mut adapter: AdapterHandle = null_mut(); 183 | 184 | let res = unsafe { 185 | ffi::create_adapter( 186 | (adapter_name.to_owned() + "\0").as_ptr() as *const c_char, 187 | (tunnel_type.to_owned() + "\0").as_ptr() as *const c_char, 188 | (guid.to_owned() + "\0").as_ptr() as *const c_char, 189 | &mut adapter, 190 | ) 191 | }; 192 | 193 | match res { 194 | SUCCESS_CODE => Ok(adapter), 195 | PARSE_GUID_ERROR_CODE => Err(Error::new(ErrorKind::Other, "Parse guid failed")), 196 | _ => Err(Error::last_os_error()) 197 | } 198 | } 199 | 200 | pub fn open_adapter(adapter_name: &str) -> Result { 201 | let mut adapter: AdapterHandle = null_mut(); 202 | 203 | let res = unsafe { 204 | ffi::open_adapter( 205 | (adapter_name.to_owned() + "\0").as_ptr() as *const c_char, 206 | &mut adapter, 207 | ) 208 | }; 209 | 210 | match res { 211 | SUCCESS_CODE => Ok(adapter), 212 | _ => Err(Error::last_os_error()) 213 | } 214 | } 215 | 216 | pub fn close_adapter(adapter: AdapterHandle) { 217 | unsafe { ffi::close_adapter(adapter) } 218 | } 219 | 220 | pub fn get_driver_version() -> Result { 221 | let mut version = 0; 222 | let res = unsafe { ffi::get_driver_version(&mut version) }; 223 | 224 | match res { 225 | SUCCESS_CODE => Ok(version), 226 | _ => Err(Error::last_os_error()) 227 | } 228 | } 229 | 230 | pub fn start_session(adapter: AdapterHandle, capacity: u32) -> Result { 231 | let mut session: Session = null_mut(); 232 | let res = unsafe { ffi::start_session(adapter, capacity, &mut session) }; 233 | 234 | match res { 235 | SUCCESS_CODE => Ok(session), 236 | _ => Err(Error::last_os_error()) 237 | } 238 | } 239 | 240 | pub fn end_session(session: Session) -> () { 241 | unsafe { ffi::end_session(session) }; 242 | } 243 | 244 | pub fn get_read_wait_event(session: Session) -> Event { 245 | unsafe { ffi::get_read_wait_event(session) } 246 | } 247 | 248 | pub fn wait_event(event: Event, time_ms: u32) -> std::result::Result<(), WaitError> { 249 | let res = unsafe { ffi::wait_event(event, time_ms) }; 250 | 251 | match res { 252 | SUCCESS_CODE => Ok(()), 253 | EVENT_WAIT_TIMEOUT => Err(WaitError::Timeout), 254 | _ => Err(WaitError::IoError(Error::last_os_error())) 255 | } 256 | } 257 | 258 | pub fn read_packet_nonblock(session: Session, buff: &mut [u8]) -> std::result::Result { 259 | let mut size = buff.len() as u32; 260 | let res_code: Code = unsafe { ffi::read_packet_nonblock(session, buff.as_mut_ptr(), &mut size) }; 261 | 262 | match res_code { 263 | SUCCESS_CODE => Ok(size as usize), 264 | NOT_ENOUGH_SIZE_CODE => Err(ReadError::NotEnoughSize(size as usize)), 265 | _ => { 266 | const ERROR_NO_MORE_ITEMS: i32 = 259; 267 | let err = Error::last_os_error(); 268 | 269 | if err.raw_os_error() == Some(ERROR_NO_MORE_ITEMS) { 270 | Err(ReadError::NoMoreItems) 271 | } else { 272 | Err(ReadError::IoError(err)) 273 | } 274 | } 275 | } 276 | } 277 | 278 | pub fn read_packet( 279 | session: Session, 280 | read_wait: Event, 281 | buff: &mut [u8], 282 | ) -> std::result::Result { 283 | let mut size = buff.len() as u32; 284 | let res_code: Code = unsafe { ffi::read_packet(session, read_wait, buff.as_mut_ptr(), &mut size) }; 285 | 286 | match res_code { 287 | SUCCESS_CODE => Ok(size as usize), 288 | NOT_ENOUGH_SIZE_CODE => Err(ReadError::NotEnoughSize(size as usize)), 289 | _ => Err(ReadError::IoError(Error::last_os_error())) 290 | } 291 | } 292 | 293 | pub fn write_packet(session: Session, buff: &[u8]) -> Result<()> { 294 | let res = unsafe { ffi::write_packet(session, buff.as_ptr(), buff.len() as u32) }; 295 | 296 | match res { 297 | SUCCESS_CODE => Ok(()), 298 | _ => Err(Error::last_os_error()) 299 | } 300 | } 301 | 302 | pub fn set_ipaddr(adapter: AdapterHandle, ipaddr: &str, subnet_mask: u8) -> Result<()> { 303 | let res = unsafe { 304 | ffi::set_ipaddr( 305 | adapter, 306 | (ipaddr.to_owned() + "\0").as_ptr() as *const c_char, 307 | subnet_mask, 308 | ) 309 | }; 310 | 311 | match res { 312 | SUCCESS_CODE => Ok(()), 313 | _ => Err(Error::new(ErrorKind::Other, "Set ip address failed")) 314 | } 315 | } 316 | 317 | pub fn get_adapter_luid(adapter: AdapterHandle) -> LUID { 318 | unsafe { 319 | ffi::get_adapter_luid(adapter) 320 | } 321 | } 322 | } 323 | 324 | pub mod adapter { 325 | use std::io::{Error, ErrorKind, Result}; 326 | use std::sync::Mutex; 327 | 328 | use once_cell::sync::Lazy; 329 | 330 | use crate::{AdapterHandle, Event, HandleWrap, LUID, raw, ReadError, Session}; 331 | 332 | fn initialize() -> Result<()> { 333 | static STATE: Lazy> = Lazy::new(|| Mutex::new(false)); 334 | let mut guard = STATE.lock().map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; 335 | 336 | if !*guard { 337 | raw::initialize()?; 338 | *guard = true; 339 | Ok(()) 340 | } else { 341 | Ok(()) 342 | } 343 | } 344 | 345 | pub struct WintunAdapter { 346 | adapter: HandleWrap, 347 | adapter_name: String, 348 | } 349 | 350 | impl Drop for WintunAdapter { 351 | fn drop(&mut self) { 352 | raw::close_adapter(self.adapter.handle); 353 | 354 | if let Err(e) = raw::delete_driver() { 355 | error!("Delete wintun driver error: {}", e); 356 | } 357 | } 358 | } 359 | 360 | impl WintunAdapter { 361 | pub fn create_adapter( 362 | adapter_name: &str, 363 | tunnel_type: &str, 364 | guid: &str, 365 | ) -> Result { 366 | initialize()?; 367 | let adapter: AdapterHandle = raw::create_adapter(adapter_name, tunnel_type, guid)?; 368 | 369 | Ok(WintunAdapter { 370 | adapter: HandleWrap::new(adapter), 371 | adapter_name: adapter_name.to_string(), 372 | }) 373 | } 374 | 375 | pub fn open_adapter(adapter_name: &str) -> Result { 376 | initialize()?; 377 | let adapter: AdapterHandle = raw::open_adapter(adapter_name)?; 378 | 379 | Ok(WintunAdapter { 380 | adapter: HandleWrap::new(adapter), 381 | adapter_name: adapter_name.to_string(), 382 | }) 383 | } 384 | 385 | pub fn get_driver_version(&self) -> Result { 386 | raw::get_driver_version() 387 | } 388 | 389 | pub fn close(self) {} 390 | 391 | pub fn get_adapter_name(&self) -> &str { 392 | &self.adapter_name 393 | } 394 | 395 | #[inline] 396 | pub fn set_ipaddr(&self, ipaddr: &str, subnet_mask: u8) -> Result<()> { 397 | raw::set_ipaddr(self.adapter.handle, ipaddr, subnet_mask) 398 | } 399 | 400 | pub fn start_session(&self, capacity: u32) -> Result { 401 | let session: Session = raw::start_session(self.adapter.handle, capacity)?; 402 | let event: Event = raw::get_read_wait_event(session); 403 | 404 | Ok(WintunStream { 405 | _adapter: self, 406 | session: HandleWrap::new(session), 407 | event: HandleWrap::new(event), 408 | }) 409 | } 410 | 411 | pub fn get_adapter_luid(&self) -> LUID { 412 | raw::get_adapter_luid(self.adapter.handle) 413 | } 414 | } 415 | 416 | pub struct WintunStream<'a> { 417 | _adapter: &'a WintunAdapter, 418 | session: HandleWrap, 419 | event: HandleWrap, 420 | } 421 | 422 | impl WintunStream<'_> { 423 | pub fn session(&self) -> Session { 424 | self.session.handle 425 | } 426 | 427 | pub fn event(&self) -> Event { 428 | self.event.handle 429 | } 430 | 431 | pub fn read_packet(&self, buff: &mut [u8]) -> std::result::Result { 432 | raw::read_packet( 433 | self.session.handle, 434 | self.event.handle, 435 | buff, 436 | ) 437 | } 438 | 439 | pub fn write_packet(&self, buff: &[u8]) -> Result<()> { 440 | raw::write_packet(self.session.handle, buff) 441 | } 442 | } 443 | 444 | impl Drop for WintunStream<'_> { 445 | fn drop(&mut self) { 446 | raw::end_session(self.session.handle) 447 | } 448 | } 449 | 450 | #[cfg(feature = "async")] 451 | mod async_mod { 452 | use std::io; 453 | 454 | use crate::{raw, ReadError, WaitError}; 455 | use crate::adapter::WintunStream; 456 | 457 | impl WintunStream<'_> { 458 | pub async fn async_read_packet(&self, buff: &mut [u8]) -> Result { 459 | loop { 460 | let res = raw::read_packet_nonblock(self.session.handle, buff); 461 | 462 | match res { 463 | Err(ReadError::NoMoreItems) => { 464 | let event = self.event; 465 | 466 | match blocking::unblock(move || { 467 | let e = event; 468 | raw::wait_event(e.handle, u32::MAX) 469 | }).await { 470 | Ok(_) => {} 471 | Err(WaitError::IoError(e)) => return Err(ReadError::IoError(e)), 472 | Err(WaitError::Timeout) => return Err(ReadError::IoError(io::Error::new(io::ErrorKind::Other, "Wait event timeout"))) 473 | }; 474 | } 475 | _ => return res 476 | } 477 | } 478 | } 479 | } 480 | } 481 | } --------------------------------------------------------------------------------