import cffi import re import enum ################################################################ # Functions and types ################################################################ LIB = """ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx typedef int BOOL; typedef unsigned char BYTE; typedef BYTE BOOLEAN; typedef void* PVOID; typedef PVOID HANDLE; typedef unsigned long DWORD; typedef unsigned long ULONG; typedef unsigned int NTSTATUS; typedef unsigned long u_long; typedef ULONG *PULONG; typedef const void *LPCVOID; typedef void *LPVOID; typedef const wchar_t *LPCWSTR; typedef uintptr_t ULONG_PTR; typedef uintptr_t UINT_PTR; typedef UINT_PTR SOCKET; typedef struct _OVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; union { struct { DWORD Offset; DWORD OffsetHigh; } DUMMYSTRUCTNAME; PVOID Pointer; } DUMMYUNIONNAME; HANDLE hEvent; } OVERLAPPED, *LPOVERLAPPED; typedef OVERLAPPED WSAOVERLAPPED; typedef LPOVERLAPPED LPWSAOVERLAPPED; typedef PVOID LPSECURITY_ATTRIBUTES; typedef PVOID LPCSTR; typedef struct _OVERLAPPED_ENTRY { ULONG_PTR lpCompletionKey; LPOVERLAPPED lpOverlapped; ULONG_PTR Internal; DWORD dwNumberOfBytesTransferred; } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; // kernel32.dll HANDLE WINAPI CreateIoCompletionPort( _In_ HANDLE FileHandle, _In_opt_ HANDLE ExistingCompletionPort, _In_ ULONG_PTR CompletionKey, _In_ DWORD NumberOfConcurrentThreads ); BOOL SetFileCompletionNotificationModes( HANDLE FileHandle, UCHAR Flags ); HANDLE CreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); BOOL WINAPI CloseHandle( _In_ HANDLE hObject ); BOOL WINAPI PostQueuedCompletionStatus( _In_ HANDLE CompletionPort, _In_ DWORD dwNumberOfBytesTransferred, _In_ ULONG_PTR dwCompletionKey, _In_opt_ LPOVERLAPPED lpOverlapped ); BOOL WINAPI GetQueuedCompletionStatusEx( _In_ HANDLE CompletionPort, _Out_ LPOVERLAPPED_ENTRY lpCompletionPortEntries, _In_ ULONG ulCount, _Out_ PULONG ulNumEntriesRemoved, _In_ DWORD dwMilliseconds, _In_ BOOL fAlertable ); BOOL WINAPI CancelIoEx( _In_ HANDLE hFile, _In_opt_ LPOVERLAPPED lpOverlapped ); BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ); BOOL ReadFile( HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ); BOOL WINAPI SetConsoleCtrlHandler( _In_opt_ void* HandlerRoutine, _In_ BOOL Add ); HANDLE CreateEventA( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName ); BOOL SetEvent( HANDLE hEvent ); BOOL ResetEvent( HANDLE hEvent ); DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); DWORD WaitForMultipleObjects( DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds ); ULONG RtlNtStatusToDosError( NTSTATUS Status ); int WSAIoctl( SOCKET s, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPWSAOVERLAPPED lpOverlapped, // actually LPWSAOVERLAPPED_COMPLETION_ROUTINE void* lpCompletionRoutine ); int WSAGetLastError(); BOOL DeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped ); // From https://github.com/piscisaureus/wepoll/blob/master/src/afd.h typedef struct _AFD_POLL_HANDLE_INFO { HANDLE Handle; ULONG Events; NTSTATUS Status; } AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; // This is really defined as a messy union to allow stuff like // i.DUMMYSTRUCTNAME.LowPart, but we don't need those complications. // Under all that it's just an int64. typedef int64_t LARGE_INTEGER; typedef struct _AFD_POLL_INFO { LARGE_INTEGER Timeout; ULONG NumberOfHandles; ULONG Exclusive; AFD_POLL_HANDLE_INFO Handles[1]; } AFD_POLL_INFO, *PAFD_POLL_INFO; """ # cribbed from pywincffi # programmatically strips out those annotations MSDN likes, like _In_ REGEX_SAL_ANNOTATION = re.compile( r"\b(_In_|_Inout_|_Out_|_Outptr_|_Reserved_)(opt_)?\b" ) LIB = REGEX_SAL_ANNOTATION.sub(" ", LIB) # Other fixups: # - get rid of FAR, cffi doesn't like it LIB = re.sub(r"\bFAR\b", " ", LIB) # - PASCAL is apparently an alias for __stdcall (on modern compilers - modern # being _MSC_VER >= 800) LIB = re.sub(r"\bPASCAL\b", "__stdcall", LIB) ffi = cffi.FFI() ffi.cdef(LIB) kernel32 = ffi.dlopen("kernel32.dll") ntdll = ffi.dlopen("ntdll.dll") ws2_32 = ffi.dlopen("ws2_32.dll") ################################################################ # Magic numbers ################################################################ # Here's a great resource for looking these up: # https://www.magnumdb.com # (Tip: check the box to see "Hex value") INVALID_HANDLE_VALUE = ffi.cast("HANDLE", -1) class ErrorCodes(enum.IntEnum): STATUS_TIMEOUT = 0x102 WAIT_TIMEOUT = 0x102 WAIT_ABANDONED = 0x80 WAIT_OBJECT_0 = 0x00 # object is signaled WAIT_FAILED = 0xFFFFFFFF ERROR_IO_PENDING = 997 ERROR_OPERATION_ABORTED = 995 ERROR_ABANDONED_WAIT_0 = 735 ERROR_INVALID_HANDLE = 6 ERROR_INVALID_PARMETER = 87 ERROR_NOT_FOUND = 1168 ERROR_NOT_SOCKET = 10038 class FileFlags(enum.IntEnum): GENERIC_READ = 0x80000000 SYNCHRONIZE = 0x00100000 FILE_FLAG_OVERLAPPED = 0x40000000 FILE_SHARE_READ = 1 FILE_SHARE_WRITE = 2 FILE_SHARE_DELETE = 4 CREATE_NEW = 1 CREATE_ALWAYS = 2 OPEN_EXISTING = 3 OPEN_ALWAYS = 4 TRUNCATE_EXISTING = 5 class AFDPollFlags(enum.IntFlag): # These are drawn from a combination of: # https://github.com/piscisaureus/wepoll/blob/master/src/afd.h # https://github.com/reactos/reactos/blob/master/sdk/include/reactos/drivers/afd/shared.h AFD_POLL_RECEIVE = 0x0001 AFD_POLL_RECEIVE_EXPEDITED = 0x0002 # OOB/urgent data AFD_POLL_SEND = 0x0004 AFD_POLL_DISCONNECT = 0x0008 # received EOF (FIN) AFD_POLL_ABORT = 0x0010 # received RST AFD_POLL_LOCAL_CLOSE = 0x0020 # local socket object closed AFD_POLL_CONNECT = 0x0040 # socket is successfully connected AFD_POLL_ACCEPT = 0x0080 # you can call accept on this socket AFD_POLL_CONNECT_FAIL = 0x0100 # connect() terminated unsuccessfully # See WSAEventSelect docs for more details on these four: AFD_POLL_QOS = 0x0200 AFD_POLL_GROUP_QOS = 0x0400 AFD_POLL_ROUTING_INTERFACE_CHANGE = 0x0800 AFD_POLL_EVENT_ADDRESS_LIST_CHANGE = 0x1000 class WSAIoctls(enum.IntEnum): SIO_BASE_HANDLE = 0x48000022 SIO_BSP_HANDLE_SELECT = 0x4800001C SIO_BSP_HANDLE_POLL = 0x4800001D class CompletionModes(enum.IntFlag): FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1 FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2 class IoControlCodes(enum.IntEnum): IOCTL_AFD_POLL = 0x00012024 ################################################################ # Generic helpers ################################################################ def _handle(obj): # For now, represent handles as either cffi HANDLEs or as ints. If you # try to pass in a file descriptor instead, it's not going to work # out. (For that msvcrt.get_osfhandle does the trick, but I don't know if # we'll actually need that for anything...) For sockets this doesn't # matter, Python never allocates an fd. So let's wait until we actually # encounter the problem before worrying about it. if type(obj) is int: return ffi.cast("HANDLE", obj) else: return obj def raise_winerror(winerror=None, *, filename=None, filename2=None): if winerror is None: winerror, msg = ffi.getwinerror() else: _, msg = ffi.getwinerror(winerror) # https://docs.python.org/3/library/exceptions.html#OSError raise OSError(0, msg, filename, winerror, filename2)