Marco Ricci commited on 2025-12-28 18:53:33
Zeige 1 geänderte Dateien mit 118 Einfügungen und 12 Löschungen.
| ... | ... |
@@ -28,7 +28,14 @@ if TYPE_CHECKING: |
| 28 | 28 |
from collections.abc import Callable |
| 29 | 29 |
from typing import ClassVar |
| 30 | 30 |
|
| 31 |
- from typing_extensions import Any, Buffer, Literal, Self, TypeVar |
|
| 31 |
+ from typing_extensions import ( |
|
| 32 |
+ Any, |
|
| 33 |
+ Buffer, |
|
| 34 |
+ Literal, |
|
| 35 |
+ Self, |
|
| 36 |
+ TypeAlias, |
|
| 37 |
+ TypeVar, |
|
| 38 |
+ ) |
|
| 32 | 39 |
|
| 33 | 40 |
from derivepassphrase import _types |
| 34 | 41 |
|
| ... | ... |
@@ -53,9 +60,43 @@ if TYPE_CHECKING: |
| 53 | 60 |
], |
| 54 | 61 |
HANDLE, |
| 55 | 62 |
] |
| 56 |
- ReadFile: Callable[[HANDLE, LPCVOID, DWORD, LPDWORD, None], BOOL] |
|
| 57 |
- WriteFile: Callable[[HANDLE, LPCVOID, DWORD, LPDWORD, None], BOOL] |
|
| 63 |
+ ReadFile: Callable[[HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED], BOOL] |
|
| 64 |
+ WriteFile: Callable[[HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED], BOOL] |
|
| 58 | 65 |
CloseHandle: Callable[[HANDLE], BOOL] |
| 66 |
+ GetOverlappedResult: Callable[[HANDLE, LPOVERLAPPED, LPDWORD, BOOL], BOOL] |
|
| 67 |
+ CreateEvent: Callable[[None, BOOL, BOOL, None], HANDLE] |
|
| 68 |
+ |
|
| 69 |
+ |
|
| 70 |
+class _OverlappedDummyStruct(ctypes.Structure): |
|
| 71 |
+ """The DUMMYSTRUCTNAME structure in the definition of OVERLAPPED.""" |
|
| 72 |
+ |
|
| 73 |
+ _fields_ = (("Offset", DWORD), ("OffsetHigh", DWORD))
|
|
| 74 |
+ |
|
| 75 |
+ |
|
| 76 |
+class _OverlappedDummyUnion(ctypes.Union): |
|
| 77 |
+ """The DUMMYUNIONNAME union in the definition of OVERLAPPED.""" |
|
| 78 |
+ |
|
| 79 |
+ _fields_ = ( |
|
| 80 |
+ ("DUMMYSTRUCTNAME", _OverlappedDummyStruct),
|
|
| 81 |
+ ("Pointer", ctypes.c_void_p),
|
|
| 82 |
+ ) |
|
| 83 |
+ |
|
| 84 |
+ |
|
| 85 |
+class OVERLAPPED(ctypes.Structure): |
|
| 86 |
+ """The data structure for overlapped (async) I/O on The Annoying OS.""" |
|
| 87 |
+ |
|
| 88 |
+ _fields_ = ( |
|
| 89 |
+ ("Internal", ctypes.POINTER(ctypes.c_ulong)),
|
|
| 90 |
+ ("InternalHigh", ctypes.POINTER(ctypes.c_ulong)),
|
|
| 91 |
+ ("DUMMYUNIONNAME", _OverlappedDummyUnion),
|
|
| 92 |
+ ("hEvent", HANDLE),
|
|
| 93 |
+ ) |
|
| 94 |
+ |
|
| 95 |
+ |
|
| 96 |
+if TYPE_CHECKING: |
|
| 97 |
+ LPOVERLAPPED: TypeAlias = ctypes._Pointer[OVERLAPPED] # noqa: SLF001 |
|
| 98 |
+else: |
|
| 99 |
+ LPOVERLAPPED = ctypes.POINTER(OVERLAPPED) |
|
| 59 | 100 |
|
| 60 | 101 |
__all__ = ("SocketProvider",)
|
| 61 | 102 |
|
| ... | ... |
@@ -90,6 +131,7 @@ GENERIC_WRITE = 0x40000000 |
| 90 | 131 |
|
| 91 | 132 |
FILE_SHARE_READ = 0x00000001 |
| 92 | 133 |
FILE_SHARE_WRITE = 0x00000002 |
| 134 |
+FILE_FLAG_OVERLAPPED = 0x40000000 |
|
| 93 | 135 |
|
| 94 | 136 |
OPEN_EXISTING = 0x3 |
| 95 | 137 |
|
| ... | ... |
@@ -101,6 +143,7 @@ CRYPTPROTECTMEMORY_CROSS_PROCESS = 0x1 |
| 101 | 143 |
PIPE_PREFIX = "//./pipe/".replace("/", "\\")
|
| 102 | 144 |
|
| 103 | 145 |
ERROR_PIPE_BUSY = 231 |
| 146 |
+ERROR_IO_PENDING = 997 |
|
| 104 | 147 |
|
| 105 | 148 |
|
| 106 | 149 |
# The type checker and the standard library use different |
| ... | ... |
@@ -172,6 +215,8 @@ try: # pragma: unless the-annoying-os no cover |
| 172 | 215 |
ReadFile = kernel32.ReadFile # type: ignore[attr-defined] |
| 173 | 216 |
WriteFile = kernel32.WriteFile # type: ignore[attr-defined] |
| 174 | 217 |
CloseHandle = kernel32.CloseHandle # type: ignore[attr-defined] |
| 218 |
+ GetOverlappedResult = kernel32.GetOverlappedResult # type: ignore[attr-defined] |
|
| 219 |
+ CreateEvent = kernel32.CreateEventW # type: ignore[attr-defined] |
|
| 175 | 220 |
except ( |
| 176 | 221 |
AttributeError, |
| 177 | 222 |
FileNotFoundError, |
| ... | ... |
@@ -203,7 +248,7 @@ except ( |
| 203 | 248 |
lpBuffer: LPVOID, # noqa: N803 |
| 204 | 249 |
nNumberOfBytesToRead: DWORD, # noqa: N803 |
| 205 | 250 |
lpNumberOfBytesRead: LPDWORD, # noqa: N803 |
| 206 |
- lpOverlapped: None, # noqa: N803 |
|
| 251 |
+ lpOverlapped: LPOVERLAPPED, # noqa: N803 |
|
| 207 | 252 |
) -> BOOL: # pragma: no cover [external] |
| 208 | 253 |
del ( |
| 209 | 254 |
hFile, |
| ... | ... |
@@ -219,7 +264,7 @@ except ( |
| 219 | 264 |
lpBuffer: LPVOID, # noqa: N803 |
| 220 | 265 |
nNumberOfBytesToWrite: DWORD, # noqa: N803 |
| 221 | 266 |
lpNumberOfBytesWritten: LPDWORD, # noqa: N803 |
| 222 |
- lpOverlapped: None, # noqa: N803 |
|
| 267 |
+ lpOverlapped: LPOVERLAPPED, # noqa: N803 |
|
| 223 | 268 |
) -> BOOL: # pragma: no cover [external] |
| 224 | 269 |
del ( |
| 225 | 270 |
hFile, |
| ... | ... |
@@ -236,6 +281,24 @@ except ( |
| 236 | 281 |
del hHandle |
| 237 | 282 |
raise OSError(errno.ENOTSUP, os.strerror(errno.ENOTSUP)) |
| 238 | 283 |
|
| 284 |
+ def GetOverlappedResult( # noqa: N802 |
|
| 285 |
+ hFile: HANDLE, # noqa: N803 |
|
| 286 |
+ lpOverlapped: LPOVERLAPPED, # noqa: N803 |
|
| 287 |
+ lpNumberOfBytesTransferred: LPDWORD, # noqa: N803 |
|
| 288 |
+ bWait: BOOL, # noqa: N803 |
|
| 289 |
+ ) -> BOOL: # pragma: no cover [external] |
|
| 290 |
+ del hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait |
|
| 291 |
+ raise OSError(errno.ENOTSUP, os.strerror(errno.ENOTSUP)) |
|
| 292 |
+ |
|
| 293 |
+ def CreateEvent( # noqa: N802 |
|
| 294 |
+ lpEventAttributes: None, # noqa: N803 |
|
| 295 |
+ bManualReset: BOOL, # noqa: N803 |
|
| 296 |
+ bInitialState: BOOL, # noqa: N803 |
|
| 297 |
+ lpName: None, # noqa: N803 |
|
| 298 |
+ ) -> HANDLE: |
|
| 299 |
+ del lpEventAttributes, bManualReset, bInitialState, lpName |
|
| 300 |
+ raise OSError(errno.ENOTSUP, os.strerror(errno.ENOTSUP)) |
|
| 301 |
+ |
|
| 239 | 302 |
|
| 240 | 303 |
else: # pragma: unless the-annoying-os no cover |
| 241 | 304 |
CreateFile.argtypes = [ # type: ignore[attr-defined] |
| ... | ... |
@@ -250,28 +313,45 @@ else: # pragma: unless the-annoying-os no cover |
| 250 | 313 |
# We always pass None/NULL. |
| 251 | 314 |
HANDLE, |
| 252 | 315 |
] |
| 316 |
+ CreateEvent.argtypes = [ |
|
| 317 |
+ # Actually, LPSECURITY_ATTRIBUTES, but we always pass |
|
| 318 |
+ # None/NULL. |
|
| 319 |
+ ctypes.c_void_p, |
|
| 320 |
+ BOOL, |
|
| 321 |
+ BOOL, |
|
| 322 |
+ # We always pass None/NULL. |
|
| 323 |
+ LPCWSTR, |
|
| 324 |
+ ] |
|
| 253 | 325 |
CreateFile.restype = HANDLE # type: ignore[attr-defined] |
| 326 |
+ CreateEvent.restype = HANDLE # type: ignore[attr-defined] |
|
| 254 | 327 |
CreateFile.errcheck = _errcheck # type: ignore[assignment, attr-defined] |
| 328 |
+ CreateEvent.errcheck = _errcheck # type: ignore[assignment, attr-defined] |
|
| 255 | 329 |
ReadFile.argtypes = [ # type: ignore[attr-defined] |
| 256 | 330 |
HANDLE, |
| 257 | 331 |
LPVOID, |
| 258 | 332 |
DWORD, |
| 259 | 333 |
LPDWORD, |
| 260 |
- # Actually, LPOVERLAPPED, but we always pass None/NULL. |
|
| 261 |
- ctypes.c_void_p, |
|
| 334 |
+ LPOVERLAPPED, |
|
| 262 | 335 |
] |
| 263 | 336 |
WriteFile.argtypes = [ # type: ignore[attr-defined] |
| 264 | 337 |
HANDLE, |
| 265 | 338 |
LPVOID, |
| 266 | 339 |
DWORD, |
| 267 | 340 |
LPDWORD, |
| 268 |
- # Actually, LPOVERLAPPED, but we always pass None/NULL. |
|
| 269 |
- ctypes.c_void_p, |
|
| 341 |
+ LPOVERLAPPED, |
|
| 270 | 342 |
] |
| 271 | 343 |
CloseHandle.argtypes = [HANDLE] # type: ignore[attr-defined] |
| 344 |
+ GetOverlappedResult.argtypes = [ |
|
| 345 |
+ HANDLE, |
|
| 346 |
+ LPOVERLAPPED, |
|
| 347 |
+ LPDWORD, |
|
| 348 |
+ # We always pass True. |
|
| 349 |
+ BOOL, |
|
| 350 |
+ ] |
|
| 272 | 351 |
ReadFile.restype = BOOL # type: ignore[attr-defined] |
| 273 | 352 |
WriteFile.restype = BOOL # type: ignore[attr-defined] |
| 274 | 353 |
CloseHandle.restype = BOOL # type: ignore[attr-defined] |
| 354 |
+ GetOverlappedResult.restype = BOOL # type: ignore[attr-defined] |
|
| 275 | 355 |
|
| 276 | 356 |
|
| 277 | 357 |
class WindowsNamedPipeHandle: |
| ... | ... |
@@ -314,7 +394,7 @@ class WindowsNamedPipeHandle: |
| 314 | 394 |
ctypes.c_ulong(FILE_SHARE_READ | FILE_SHARE_WRITE), |
| 315 | 395 |
None, |
| 316 | 396 |
ctypes.c_ulong(OPEN_EXISTING), |
| 317 |
- ctypes.c_ulong(0), |
|
| 397 |
+ ctypes.c_ulong(FILE_FLAG_OVERLAPPED), |
|
| 318 | 398 |
None, |
| 319 | 399 |
) |
| 320 | 400 |
except BlockingIOError: # pragma: no cover [external] |
| ... | ... |
@@ -344,17 +424,31 @@ class WindowsNamedPipeHandle: |
| 344 | 424 |
buffer = (ctypes.c_char * 65536)() |
| 345 | 425 |
while data > 0: |
| 346 | 426 |
block_size = min(max(0, data), 65536) |
| 427 |
+ wait_event = CreateEvent(None, BOOL(True), BOOL(False), None) # noqa: FBT003 |
|
| 428 |
+ try: |
|
| 429 |
+ overlapped_struct = OVERLAPPED(hEvent=wait_event) |
|
| 347 | 430 |
success = ReadFile( |
| 348 | 431 |
self.handle, |
| 349 | 432 |
ctypes.cast(ctypes.byref(buffer), ctypes.c_void_p), |
| 350 | 433 |
DWORD(block_size), |
| 351 | 434 |
ctypes.cast(ctypes.byref(read_count), LPDWORD), |
| 352 |
- None, |
|
| 435 |
+ ctypes.cast(ctypes.byref(overlapped_struct), LPOVERLAPPED), |
|
| 436 |
+ ) |
|
| 437 |
+ if not success and ctypes.GetLastError() == ERROR_IO_PENDING: |
|
| 438 |
+ success = GetOverlappedResult( |
|
| 439 |
+ self.handle, |
|
| 440 |
+ ctypes.cast( |
|
| 441 |
+ ctypes.byref(overlapped_struct), LPOVERLAPPED |
|
| 442 |
+ ), |
|
| 443 |
+ ctypes.cast(ctypes.byref(read_count), LPDWORD), |
|
| 444 |
+ BOOL(True), # noqa: FBT003 |
|
| 353 | 445 |
) |
| 354 | 446 |
if ( |
| 355 | 447 |
not success or read_count.value == 0 |
| 356 | 448 |
): # pragma: no cover [external] |
| 357 | 449 |
raise ctypes.WinError() # type: ignore[attr-defined] |
| 450 |
+ finally: |
|
| 451 |
+ CloseHandle(wait_event) |
|
| 358 | 452 |
result.extend(buffer.raw[:block_size]) |
| 359 | 453 |
data -= read_count.value |
| 360 | 454 |
read_count.value = 0 |
| ... | ... |
@@ -369,17 +463,29 @@ class WindowsNamedPipeHandle: |
| 369 | 463 |
for i, x in enumerate(data): |
| 370 | 464 |
databuf[i] = ctypes.c_char(x) |
| 371 | 465 |
write_count = DWORD(0) |
| 466 |
+ wait_event = CreateEvent(None, BOOL(True), BOOL(False), None) # noqa: FBT003 |
|
| 467 |
+ try: |
|
| 468 |
+ overlapped_struct = OVERLAPPED(hEvent=wait_event) |
|
| 372 | 469 |
success = WriteFile( |
| 373 | 470 |
self.handle, |
| 374 | 471 |
ctypes.cast(ctypes.byref(databuf), ctypes.c_void_p), |
| 375 | 472 |
DWORD(len(data)), |
| 376 | 473 |
ctypes.cast(ctypes.byref(write_count), LPDWORD), |
| 377 |
- None, |
|
| 474 |
+ ctypes.cast(ctypes.byref(overlapped_struct), LPOVERLAPPED), |
|
| 475 |
+ ) |
|
| 476 |
+ if not success and ctypes.GetLastError() == ERROR_IO_PENDING: |
|
| 477 |
+ success = GetOverlappedResult( |
|
| 478 |
+ self.handle, |
|
| 479 |
+ ctypes.cast(ctypes.byref(overlapped_struct), LPOVERLAPPED), |
|
| 480 |
+ ctypes.cast(ctypes.byref(write_count), LPDWORD), |
|
| 481 |
+ BOOL(True), # noqa: FBT003 |
|
| 378 | 482 |
) |
| 379 | 483 |
if ( |
| 380 | 484 |
not success or write_count.value == 0 |
| 381 | 485 |
): # pragma: no cover [external] |
| 382 | 486 |
raise ctypes.WinError() # type: ignore[attr-defined] |
| 487 |
+ finally: |
|
| 488 |
+ CloseHandle(wait_event) |
|
| 383 | 489 |
|
| 384 | 490 |
@classmethod |
| 385 | 491 |
def for_openssh(cls) -> Self: |
| 386 | 492 |