diff options
author | AlexSm <alex@ydb.tech> | 2024-03-05 10:40:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-05 12:40:59 +0300 |
commit | 1ac13c847b5358faba44dbb638a828e24369467b (patch) | |
tree | 07672b4dd3604ad3dee540a02c6494cb7d10dc3d /contrib/tools/python3/Modules/overlapped.c | |
parent | ffcca3e7f7958ddc6487b91d3df8c01054bd0638 (diff) | |
download | ydb-1ac13c847b5358faba44dbb638a828e24369467b.tar.gz |
Library import 16 (#2433)
Co-authored-by: robot-piglet <robot-piglet@yandex-team.com>
Co-authored-by: deshevoy <deshevoy@yandex-team.com>
Co-authored-by: robot-contrib <robot-contrib@yandex-team.com>
Co-authored-by: thegeorg <thegeorg@yandex-team.com>
Co-authored-by: robot-ya-builder <robot-ya-builder@yandex-team.com>
Co-authored-by: svidyuk <svidyuk@yandex-team.com>
Co-authored-by: shadchin <shadchin@yandex-team.com>
Co-authored-by: robot-ratatosk <robot-ratatosk@yandex-team.com>
Co-authored-by: innokentii <innokentii@yandex-team.com>
Co-authored-by: arkady-e1ppa <arkady-e1ppa@yandex-team.com>
Co-authored-by: snermolaev <snermolaev@yandex-team.com>
Co-authored-by: dimdim11 <dimdim11@yandex-team.com>
Co-authored-by: kickbutt <kickbutt@yandex-team.com>
Co-authored-by: abdullinsaid <abdullinsaid@yandex-team.com>
Co-authored-by: korsunandrei <korsunandrei@yandex-team.com>
Co-authored-by: petrk <petrk@yandex-team.com>
Co-authored-by: miroslav2 <miroslav2@yandex-team.com>
Co-authored-by: serjflint <serjflint@yandex-team.com>
Co-authored-by: akhropov <akhropov@yandex-team.com>
Co-authored-by: prettyboy <prettyboy@yandex-team.com>
Co-authored-by: ilikepugs <ilikepugs@yandex-team.com>
Co-authored-by: hiddenpath <hiddenpath@yandex-team.com>
Co-authored-by: mikhnenko <mikhnenko@yandex-team.com>
Co-authored-by: spreis <spreis@yandex-team.com>
Co-authored-by: andreyshspb <andreyshspb@yandex-team.com>
Co-authored-by: dimaandreev <dimaandreev@yandex-team.com>
Co-authored-by: rashid <rashid@yandex-team.com>
Co-authored-by: robot-ydb-importer <robot-ydb-importer@yandex-team.com>
Co-authored-by: r-vetrov <r-vetrov@yandex-team.com>
Co-authored-by: ypodlesov <ypodlesov@yandex-team.com>
Co-authored-by: zaverden <zaverden@yandex-team.com>
Co-authored-by: vpozdyayev <vpozdyayev@yandex-team.com>
Co-authored-by: robot-cozmo <robot-cozmo@yandex-team.com>
Co-authored-by: v-korovin <v-korovin@yandex-team.com>
Co-authored-by: arikon <arikon@yandex-team.com>
Co-authored-by: khoden <khoden@yandex-team.com>
Co-authored-by: psydmm <psydmm@yandex-team.com>
Co-authored-by: robot-javacom <robot-javacom@yandex-team.com>
Co-authored-by: dtorilov <dtorilov@yandex-team.com>
Co-authored-by: sennikovmv <sennikovmv@yandex-team.com>
Co-authored-by: hcpp <hcpp@ydb.tech>
Diffstat (limited to 'contrib/tools/python3/Modules/overlapped.c')
-rw-r--r-- | contrib/tools/python3/Modules/overlapped.c | 2069 |
1 files changed, 2069 insertions, 0 deletions
diff --git a/contrib/tools/python3/Modules/overlapped.c b/contrib/tools/python3/Modules/overlapped.c new file mode 100644 index 0000000000..afdd78d1bc --- /dev/null +++ b/contrib/tools/python3/Modules/overlapped.c @@ -0,0 +1,2069 @@ +/* + * Support for overlapped IO + * + * Some code borrowed from Modules/_winapi.c of CPython + */ + +/* XXX check overflow and DWORD <-> Py_ssize_t conversions + Check itemsize */ + +#include "Python.h" +#include "structmember.h" // PyMemberDef + +#define WINDOWS_LEAN_AND_MEAN +#include <winsock2.h> +#include <ws2tcpip.h> +#include <mswsock.h> + +#if defined(MS_WIN32) && !defined(MS_WIN64) +# define F_POINTER "k" +# define T_POINTER T_ULONG +#else +# define F_POINTER "K" +# define T_POINTER T_ULONGLONG +#endif + +#define F_HANDLE F_POINTER +#define F_ULONG_PTR F_POINTER +#define F_DWORD "k" +#define F_BOOL "i" +#define F_UINT "I" + +#define T_HANDLE T_POINTER + +/*[python input] +class pointer_converter(CConverter): + format_unit = '"F_POINTER"' + + def parse_arg(self, argname, displayname): + return """ + {paramname} = PyLong_AsVoidPtr({argname}); + if (!{paramname} && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.parser_name) + +class OVERLAPPED_converter(pointer_converter): + type = 'OVERLAPPED *' + +class HANDLE_converter(pointer_converter): + type = 'HANDLE' + +class ULONG_PTR_converter(pointer_converter): + type = 'ULONG_PTR' + + def parse_arg(self, argname, displayname): + return """ + {paramname} = (uintptr_t)PyLong_AsVoidPtr({argname}); + if (!{paramname} && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.parser_name) + +class DWORD_converter(unsigned_long_converter): + type = 'DWORD' + +class BOOL_converter(int_converter): + type = 'BOOL' +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=8a07ea3018f4cec8]*/ + +/*[clinic input] +module _overlapped +class _overlapped.Overlapped "OverlappedObject *" "&OverlappedType" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=92e5a799db35b96c]*/ + + +enum {TYPE_NONE, TYPE_NOT_STARTED, TYPE_READ, TYPE_READINTO, TYPE_WRITE, + TYPE_ACCEPT, TYPE_CONNECT, TYPE_DISCONNECT, TYPE_CONNECT_NAMED_PIPE, + TYPE_WAIT_NAMED_PIPE_AND_CONNECT, TYPE_TRANSMIT_FILE, TYPE_READ_FROM, + TYPE_WRITE_TO, TYPE_READ_FROM_INTO}; + +typedef struct { + PyObject_HEAD + OVERLAPPED overlapped; + /* For convenience, we store the file handle too */ + HANDLE handle; + /* Error returned by last method call */ + DWORD error; + /* Type of operation */ + DWORD type; + union { + /* Buffer allocated by us: TYPE_READ and TYPE_ACCEPT */ + PyObject *allocated_buffer; + /* Buffer passed by the user: TYPE_WRITE, TYPE_WRITE_TO, and TYPE_READINTO */ + Py_buffer user_buffer; + + /* Data used for reading from a connectionless socket: + TYPE_READ_FROM */ + struct { + // A (buffer, (host, port)) tuple + PyObject *result; + // The actual read buffer + PyObject *allocated_buffer; + struct sockaddr_in6 address; + int address_length; + } read_from; + + /* Data used for reading from a connectionless socket: + TYPE_READ_FROM_INTO */ + struct { + // A (number of bytes read, (host, port)) tuple + PyObject* result; + /* Buffer passed by the user */ + Py_buffer user_buffer; + struct sockaddr_in6 address; + int address_length; + } read_from_into; + }; +} OverlappedObject; + + +static inline void +steal_buffer(Py_buffer * dst, Py_buffer * src) +{ + memcpy(dst, src, sizeof(Py_buffer)); + memset(src, 0, sizeof(Py_buffer)); +} + +/* + * Map Windows error codes to subclasses of OSError + */ + +static PyObject * +SetFromWindowsErr(DWORD err) +{ + PyObject *exception_type; + + if (err == 0) + err = GetLastError(); + switch (err) { + case ERROR_CONNECTION_REFUSED: + exception_type = PyExc_ConnectionRefusedError; + break; + case ERROR_CONNECTION_ABORTED: + exception_type = PyExc_ConnectionAbortedError; + break; + default: + exception_type = PyExc_OSError; + } + return PyErr_SetExcFromWindowsErr(exception_type, err); +} + +/* + * Some functions should be loaded at runtime + */ + +static LPFN_ACCEPTEX Py_AcceptEx = NULL; +static LPFN_CONNECTEX Py_ConnectEx = NULL; +static LPFN_DISCONNECTEX Py_DisconnectEx = NULL; +static LPFN_TRANSMITFILE Py_TransmitFile = NULL; + +#define GET_WSA_POINTER(s, x) \ + (SOCKET_ERROR != WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, \ + &Guid##x, sizeof(Guid##x), &Py_##x, \ + sizeof(Py_##x), &dwBytes, NULL, NULL)) + +static int +initialize_function_pointers(void) +{ + GUID GuidAcceptEx = WSAID_ACCEPTEX; + GUID GuidConnectEx = WSAID_CONNECTEX; + GUID GuidDisconnectEx = WSAID_DISCONNECTEX; + GUID GuidTransmitFile = WSAID_TRANSMITFILE; + SOCKET s; + DWORD dwBytes; + + if (Py_AcceptEx != NULL && + Py_ConnectEx != NULL && + Py_DisconnectEx != NULL && + Py_TransmitFile != NULL) + { + // All function pointers are initialized already + // by previous module import + return 0; + } + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + SetFromWindowsErr(WSAGetLastError()); + return -1; + } + + if (!GET_WSA_POINTER(s, AcceptEx) || + !GET_WSA_POINTER(s, ConnectEx) || + !GET_WSA_POINTER(s, DisconnectEx) || + !GET_WSA_POINTER(s, TransmitFile)) + { + closesocket(s); + SetFromWindowsErr(WSAGetLastError()); + return -1; + } + + closesocket(s); + return 0; +} + +/* + * Completion port stuff + */ + +/*[clinic input] +_overlapped.CreateIoCompletionPort + + handle as FileHandle: HANDLE + port as ExistingCompletionPort: HANDLE + key as CompletionKey: ULONG_PTR + concurrency as NumberOfConcurrentThreads: DWORD + / + +Create a completion port or register a handle with a port. +[clinic start generated code]*/ + +static PyObject * +_overlapped_CreateIoCompletionPort_impl(PyObject *module, HANDLE FileHandle, + HANDLE ExistingCompletionPort, + ULONG_PTR CompletionKey, + DWORD NumberOfConcurrentThreads) +/*[clinic end generated code: output=24ede2b0f05e5433 input=847bae4d0efe1976]*/ +{ + HANDLE ret; + + Py_BEGIN_ALLOW_THREADS + ret = CreateIoCompletionPort(FileHandle, ExistingCompletionPort, + CompletionKey, NumberOfConcurrentThreads); + Py_END_ALLOW_THREADS + + if (ret == NULL) + return SetFromWindowsErr(0); + return Py_BuildValue(F_HANDLE, ret); +} + +/*[clinic input] +_overlapped.GetQueuedCompletionStatus + + port as CompletionPort: HANDLE + msecs as Milliseconds: DWORD + / + +Get a message from completion port. + +Wait for up to msecs milliseconds. +[clinic start generated code]*/ + +static PyObject * +_overlapped_GetQueuedCompletionStatus_impl(PyObject *module, + HANDLE CompletionPort, + DWORD Milliseconds) +/*[clinic end generated code: output=68314171628dddb7 input=94a042d14c4f6410]*/ +{ + DWORD NumberOfBytes = 0; + ULONG_PTR CompletionKey = 0; + OVERLAPPED *Overlapped = NULL; + DWORD err; + BOOL ret; + + Py_BEGIN_ALLOW_THREADS + ret = GetQueuedCompletionStatus(CompletionPort, &NumberOfBytes, + &CompletionKey, &Overlapped, Milliseconds); + Py_END_ALLOW_THREADS + + err = ret ? ERROR_SUCCESS : GetLastError(); + if (Overlapped == NULL) { + if (err == WAIT_TIMEOUT) + Py_RETURN_NONE; + else + return SetFromWindowsErr(err); + } + return Py_BuildValue(F_DWORD F_DWORD F_ULONG_PTR F_POINTER, + err, NumberOfBytes, CompletionKey, Overlapped); +} + +/*[clinic input] +_overlapped.PostQueuedCompletionStatus + + port as CompletionPort: HANDLE + bytes as NumberOfBytes: DWORD + key as CompletionKey: ULONG_PTR + address as Overlapped: OVERLAPPED + / + +Post a message to completion port. +[clinic start generated code]*/ + +static PyObject * +_overlapped_PostQueuedCompletionStatus_impl(PyObject *module, + HANDLE CompletionPort, + DWORD NumberOfBytes, + ULONG_PTR CompletionKey, + OVERLAPPED *Overlapped) +/*[clinic end generated code: output=93e73f2933a43e9e input=e936202d87937aca]*/ +{ + BOOL ret; + + Py_BEGIN_ALLOW_THREADS + ret = PostQueuedCompletionStatus(CompletionPort, NumberOfBytes, + CompletionKey, Overlapped); + Py_END_ALLOW_THREADS + + if (!ret) + return SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +/* + * Wait for a handle + */ + +struct PostCallbackData { + HANDLE CompletionPort; + LPOVERLAPPED Overlapped; +}; + +static VOID CALLBACK +PostToQueueCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) +{ + struct PostCallbackData *p = (struct PostCallbackData*) lpParameter; + + PostQueuedCompletionStatus(p->CompletionPort, TimerOrWaitFired, + 0, p->Overlapped); + /* ignore possible error! */ + PyMem_RawFree(p); +} + +/*[clinic input] +_overlapped.RegisterWaitWithQueue + + Object: HANDLE + CompletionPort: HANDLE + Overlapped: OVERLAPPED + Timeout as Milliseconds: DWORD + / + +Register wait for Object; when complete CompletionPort is notified. +[clinic start generated code]*/ + +static PyObject * +_overlapped_RegisterWaitWithQueue_impl(PyObject *module, HANDLE Object, + HANDLE CompletionPort, + OVERLAPPED *Overlapped, + DWORD Milliseconds) +/*[clinic end generated code: output=c2ace732e447fe45 input=2dd4efee44abe8ee]*/ +{ + HANDLE NewWaitObject; + struct PostCallbackData data = {CompletionPort, Overlapped}, *pdata; + + /* Use PyMem_RawMalloc() rather than PyMem_Malloc(), since + PostToQueueCallback() will call PyMem_Free() from a new C thread + which doesn't hold the GIL. */ + pdata = PyMem_RawMalloc(sizeof(struct PostCallbackData)); + if (pdata == NULL) + return SetFromWindowsErr(0); + + *pdata = data; + + if (!RegisterWaitForSingleObject( + &NewWaitObject, Object, PostToQueueCallback, pdata, Milliseconds, + WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) + { + SetFromWindowsErr(0); + PyMem_RawFree(pdata); + return NULL; + } + + return Py_BuildValue(F_HANDLE, NewWaitObject); +} + +/*[clinic input] +_overlapped.UnregisterWait + + WaitHandle: HANDLE + / + +Unregister wait handle. +[clinic start generated code]*/ + +static PyObject * +_overlapped_UnregisterWait_impl(PyObject *module, HANDLE WaitHandle) +/*[clinic end generated code: output=ec90cd955a9a617d input=a56709544cb2df0f]*/ +{ + BOOL ret; + + Py_BEGIN_ALLOW_THREADS + ret = UnregisterWait(WaitHandle); + Py_END_ALLOW_THREADS + + if (!ret) + return SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +/*[clinic input] +_overlapped.UnregisterWaitEx + + WaitHandle: HANDLE + Event: HANDLE + / + +Unregister wait handle. +[clinic start generated code]*/ + +static PyObject * +_overlapped_UnregisterWaitEx_impl(PyObject *module, HANDLE WaitHandle, + HANDLE Event) +/*[clinic end generated code: output=2e3d84c1d5f65b92 input=953cddc1de50fab9]*/ +{ + BOOL ret; + + Py_BEGIN_ALLOW_THREADS + ret = UnregisterWaitEx(WaitHandle, Event); + Py_END_ALLOW_THREADS + + if (!ret) + return SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +/* + * Event functions -- currently only used by tests + */ + +/*[clinic input] +_overlapped.CreateEvent + + EventAttributes: object + ManualReset: BOOL + InitialState: BOOL + Name: Py_UNICODE(accept={str, NoneType}) + / + +Create an event. + +EventAttributes must be None. +[clinic start generated code]*/ + +static PyObject * +_overlapped_CreateEvent_impl(PyObject *module, PyObject *EventAttributes, + BOOL ManualReset, BOOL InitialState, + const Py_UNICODE *Name) +/*[clinic end generated code: output=8e04f0916c17b13d input=dbc36ae14375ba24]*/ +{ + HANDLE Event; + + if (EventAttributes != Py_None) { + PyErr_SetString(PyExc_ValueError, "EventAttributes must be None"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + Event = CreateEventW(NULL, ManualReset, InitialState, Name); + Py_END_ALLOW_THREADS + + if (Event == NULL) + return SetFromWindowsErr(0); + return Py_BuildValue(F_HANDLE, Event); +} + +/*[clinic input] +_overlapped.SetEvent + + Handle: HANDLE + / + +Set event. +[clinic start generated code]*/ + +static PyObject * +_overlapped_SetEvent_impl(PyObject *module, HANDLE Handle) +/*[clinic end generated code: output=5b8d974216b0e569 input=d8b0d26eb7391e80]*/ +{ + BOOL ret; + + Py_BEGIN_ALLOW_THREADS + ret = SetEvent(Handle); + Py_END_ALLOW_THREADS + + if (!ret) + return SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +/*[clinic input] +_overlapped.ResetEvent + + Handle: HANDLE + / + +Reset event. +[clinic start generated code]*/ + +static PyObject * +_overlapped_ResetEvent_impl(PyObject *module, HANDLE Handle) +/*[clinic end generated code: output=066537a8405cddb2 input=d4e089c9ba84ff2f]*/ +{ + BOOL ret; + + Py_BEGIN_ALLOW_THREADS + ret = ResetEvent(Handle); + Py_END_ALLOW_THREADS + + if (!ret) + return SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +/* + * Bind socket handle to local port without doing slow getaddrinfo() + */ + +/*[clinic input] +_overlapped.BindLocal + + handle as Socket: HANDLE + family as Family: int + / + +Bind a socket handle to an arbitrary local port. + +family should be AF_INET or AF_INET6. +[clinic start generated code]*/ + +static PyObject * +_overlapped_BindLocal_impl(PyObject *module, HANDLE Socket, int Family) +/*[clinic end generated code: output=edb93862697aed9c input=a0e7b5c2f541170c]*/ +{ + BOOL ret; + + if (Family == AF_INET) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.S_un.S_addr = INADDR_ANY; + ret = bind((SOCKET)Socket, (SOCKADDR*)&addr, sizeof(addr)) + != SOCKET_ERROR; + } else if (Family == AF_INET6) { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = 0; + addr.sin6_addr = in6addr_any; + ret = bind((SOCKET)Socket, (SOCKADDR*)&addr, sizeof(addr)) + != SOCKET_ERROR; + } else { + PyErr_SetString(PyExc_ValueError, "expected tuple of length 2 or 4"); + return NULL; + } + + if (!ret) + return SetFromWindowsErr(WSAGetLastError()); + Py_RETURN_NONE; +} + +/* + * Windows equivalent of os.strerror() -- compare _ctypes/callproc.c + */ + +/*[clinic input] +_overlapped.FormatMessage + + error_code as code: DWORD + / + +Return error message for an error code. +[clinic start generated code]*/ + +static PyObject * +_overlapped_FormatMessage_impl(PyObject *module, DWORD code) +/*[clinic end generated code: output=02c964ff22407c6b input=644bb5b80326179e]*/ +{ + DWORD n; + WCHAR *lpMsgBuf; + PyObject *res; + + n = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, + NULL); + if (n) { + while (iswspace(lpMsgBuf[n-1])) + --n; + lpMsgBuf[n] = L'\0'; + res = Py_BuildValue("u", lpMsgBuf); + } else { + res = PyUnicode_FromFormat("unknown error code %u", code); + } + LocalFree(lpMsgBuf); + return res; +} + + +/* + * Mark operation as completed - used when reading produces ERROR_BROKEN_PIPE + */ + +static inline void +mark_as_completed(OVERLAPPED *ov) +{ + ov->Internal = 0; + if (ov->hEvent != NULL) + SetEvent(ov->hEvent); +} + +/* + * A Python object wrapping an OVERLAPPED structure and other useful data + * for overlapped I/O + */ + +/*[clinic input] +@classmethod +_overlapped.Overlapped.__new__ + + event: HANDLE(c_default='INVALID_HANDLE_VALUE') = _overlapped.INVALID_HANDLE_VALUE + +OVERLAPPED structure wrapper. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_impl(PyTypeObject *type, HANDLE event) +/*[clinic end generated code: output=6da60504a18eb421 input=26b8a7429e629e95]*/ +{ + OverlappedObject *self; + + if (event == INVALID_HANDLE_VALUE) { + event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (event == NULL) + return SetFromWindowsErr(0); + } + + self = PyObject_New(OverlappedObject, type); + if (self == NULL) { + if (event != NULL) + CloseHandle(event); + return NULL; + } + + self->handle = NULL; + self->error = 0; + self->type = TYPE_NONE; + self->allocated_buffer = NULL; + memset(&self->overlapped, 0, sizeof(OVERLAPPED)); + memset(&self->user_buffer, 0, sizeof(Py_buffer)); + if (event) + self->overlapped.hEvent = event; + return (PyObject *)self; +} + + +/* Note (bpo-32710): OverlappedType.tp_clear is not defined to not release + buffers while overlapped are still running, to prevent a crash. */ +static int +Overlapped_clear(OverlappedObject *self) +{ + switch (self->type) { + case TYPE_READ: + case TYPE_ACCEPT: { + Py_CLEAR(self->allocated_buffer); + break; + } + case TYPE_READ_FROM: { + // An initial call to WSARecvFrom will only allocate the buffer. + // The result tuple of (message, address) is only + // allocated _after_ a message has been received. + if(self->read_from.result) { + // We've received a message, free the result tuple. + Py_CLEAR(self->read_from.result); + } + if(self->read_from.allocated_buffer) { + Py_CLEAR(self->read_from.allocated_buffer); + } + break; + } + case TYPE_READ_FROM_INTO: { + if (self->read_from_into.result) { + // We've received a message, free the result tuple. + Py_CLEAR(self->read_from_into.result); + } + if (self->read_from_into.user_buffer.obj) { + PyBuffer_Release(&self->read_from_into.user_buffer); + } + break; + } + case TYPE_WRITE: + case TYPE_WRITE_TO: + case TYPE_READINTO: { + if (self->user_buffer.obj) { + PyBuffer_Release(&self->user_buffer); + } + break; + } + } + self->type = TYPE_NOT_STARTED; + return 0; +} + +static void +Overlapped_dealloc(OverlappedObject *self) +{ + DWORD bytes; + DWORD olderr = GetLastError(); + BOOL wait = FALSE; + BOOL ret; + + if (!HasOverlappedIoCompleted(&self->overlapped) && + self->type != TYPE_NOT_STARTED) + { + Py_BEGIN_ALLOW_THREADS + if (CancelIoEx(self->handle, &self->overlapped)) + wait = TRUE; + + ret = GetOverlappedResult(self->handle, &self->overlapped, + &bytes, wait); + Py_END_ALLOW_THREADS + + switch (ret ? ERROR_SUCCESS : GetLastError()) { + case ERROR_SUCCESS: + case ERROR_NOT_FOUND: + case ERROR_OPERATION_ABORTED: + break; + default: + PyErr_Format( + PyExc_RuntimeError, + "%R still has pending operation at " + "deallocation, the process may crash", self); + PyErr_WriteUnraisable(NULL); + } + } + + if (self->overlapped.hEvent != NULL) { + CloseHandle(self->overlapped.hEvent); + } + + Overlapped_clear(self); + SetLastError(olderr); + + PyTypeObject *tp = Py_TYPE(self); + PyObject_Free(self); + Py_DECREF(tp); +} + + +/* Convert IPv4 sockaddr to a Python str. */ + +static PyObject * +make_ipv4_addr(const struct sockaddr_in *addr) +{ + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)) == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return PyUnicode_FromString(buf); +} + +/* Convert IPv6 sockaddr to a Python str. */ + +static PyObject * +make_ipv6_addr(const struct sockaddr_in6 *addr) +{ + char buf[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof(buf)) == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return PyUnicode_FromString(buf); +} + +static PyObject* +unparse_address(LPSOCKADDR Address, DWORD Length) +{ + /* The function is adopted from mocketmodule.c makesockaddr()*/ + + switch(Address->sa_family) { + case AF_INET: { + const struct sockaddr_in *a = (const struct sockaddr_in *)Address; + PyObject *addrobj = make_ipv4_addr(a); + PyObject *ret = NULL; + if (addrobj) { + ret = Py_BuildValue("Oi", addrobj, ntohs(a->sin_port)); + Py_DECREF(addrobj); + } + return ret; + } + case AF_INET6: { + const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)Address; + PyObject *addrobj = make_ipv6_addr(a); + PyObject *ret = NULL; + if (addrobj) { + ret = Py_BuildValue("OiII", + addrobj, + ntohs(a->sin6_port), + ntohl(a->sin6_flowinfo), + a->sin6_scope_id); + Py_DECREF(addrobj); + } + return ret; + } + default: { + PyErr_SetString(PyExc_ValueError, "recvfrom returned unsupported address family"); + return NULL; + } + } +} + +/*[clinic input] +_overlapped.Overlapped.cancel + +Cancel overlapped operation. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_cancel_impl(OverlappedObject *self) +/*[clinic end generated code: output=54ad7aeece89901c input=80eb67c7b57dbcf1]*/ +{ + BOOL ret = TRUE; + + if (self->type == TYPE_NOT_STARTED + || self->type == TYPE_WAIT_NAMED_PIPE_AND_CONNECT) + Py_RETURN_NONE; + + if (!HasOverlappedIoCompleted(&self->overlapped)) { + Py_BEGIN_ALLOW_THREADS + ret = CancelIoEx(self->handle, &self->overlapped); + Py_END_ALLOW_THREADS + } + + /* CancelIoEx returns ERROR_NOT_FOUND if the I/O completed in-between */ + if (!ret && GetLastError() != ERROR_NOT_FOUND) + return SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +/*[clinic input] +_overlapped.Overlapped.getresult + + wait: BOOL(c_default='FALSE') = False + / + +Retrieve result of operation. + +If wait is true then it blocks until the operation is finished. If wait +is false and the operation is still pending then an error is raised. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait) +/*[clinic end generated code: output=8c9bd04d08994f6c input=aa5b03e9897ca074]*/ +{ + DWORD transferred = 0; + BOOL ret; + DWORD err; + PyObject *addr; + + if (self->type == TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation not yet attempted"); + return NULL; + } + + if (self->type == TYPE_NOT_STARTED) { + PyErr_SetString(PyExc_ValueError, "operation failed to start"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + ret = GetOverlappedResult(self->handle, &self->overlapped, &transferred, + wait); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : GetLastError(); + switch (err) { + case ERROR_SUCCESS: + case ERROR_MORE_DATA: + break; + case ERROR_BROKEN_PIPE: + if (self->type == TYPE_READ || self->type == TYPE_READINTO) { + break; + } + else if (self->type == TYPE_READ_FROM && + (self->read_from.result != NULL || + self->read_from.allocated_buffer != NULL)) + { + break; + } + else if (self->type == TYPE_READ_FROM_INTO && + self->read_from_into.result != NULL) + { + break; + } + /* fall through */ + default: + return SetFromWindowsErr(err); + } + + switch (self->type) { + case TYPE_READ: + assert(PyBytes_CheckExact(self->allocated_buffer)); + if (transferred != PyBytes_GET_SIZE(self->allocated_buffer) && + _PyBytes_Resize(&self->allocated_buffer, transferred)) + return NULL; + + return Py_NewRef(self->allocated_buffer); + case TYPE_READ_FROM: + assert(PyBytes_CheckExact(self->read_from.allocated_buffer)); + + if (transferred != PyBytes_GET_SIZE( + self->read_from.allocated_buffer) && + _PyBytes_Resize(&self->read_from.allocated_buffer, transferred)) + { + return NULL; + } + + // unparse the address + addr = unparse_address((SOCKADDR*)&self->read_from.address, + self->read_from.address_length); + + if (addr == NULL) { + return NULL; + } + + // The result is a two item tuple: (message, address) + self->read_from.result = PyTuple_New(2); + if (self->read_from.result == NULL) { + Py_CLEAR(addr); + return NULL; + } + + // first item: message + PyTuple_SET_ITEM(self->read_from.result, 0, + Py_NewRef(self->read_from.allocated_buffer)); + // second item: address + PyTuple_SET_ITEM(self->read_from.result, 1, addr); + + return Py_NewRef(self->read_from.result); + case TYPE_READ_FROM_INTO: + // unparse the address + addr = unparse_address((SOCKADDR*)&self->read_from_into.address, + self->read_from_into.address_length); + + if (addr == NULL) { + return NULL; + } + + // The result is a two item tuple: (number of bytes read, address) + self->read_from_into.result = PyTuple_New(2); + if (self->read_from_into.result == NULL) { + Py_CLEAR(addr); + return NULL; + } + + // first item: number of bytes read + PyTuple_SET_ITEM(self->read_from_into.result, 0, + PyLong_FromUnsignedLong((unsigned long)transferred)); + // second item: address + PyTuple_SET_ITEM(self->read_from_into.result, 1, addr); + + return Py_NewRef(self->read_from_into.result); + default: + return PyLong_FromUnsignedLong((unsigned long) transferred); + } +} + +static PyObject * +do_ReadFile(OverlappedObject *self, HANDLE handle, + char *bufstart, DWORD buflen) +{ + DWORD nread; + int ret; + DWORD err; + + Py_BEGIN_ALLOW_THREADS + ret = ReadFile(handle, bufstart, buflen, &nread, + &self->overlapped); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : GetLastError(); + switch (err) { + case ERROR_BROKEN_PIPE: + mark_as_completed(&self->overlapped); + return SetFromWindowsErr(err); + case ERROR_SUCCESS: + case ERROR_MORE_DATA: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.ReadFile + + handle: HANDLE + size: DWORD + / + +Start overlapped read. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_ReadFile_impl(OverlappedObject *self, HANDLE handle, + DWORD size) +/*[clinic end generated code: output=4c8557e16941e4ae input=98c495baa0342425]*/ +{ + PyObject *buf; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T <= SIZEOF_LONG + size = Py_MIN(size, (DWORD)PY_SSIZE_T_MAX); +#endif + buf = PyBytes_FromStringAndSize(NULL, Py_MAX(size, 1)); + if (buf == NULL) + return NULL; + + self->type = TYPE_READ; + self->handle = handle; + self->allocated_buffer = buf; + + return do_ReadFile(self, handle, PyBytes_AS_STRING(buf), size); +} + +/*[clinic input] +_overlapped.Overlapped.ReadFileInto + + handle: HANDLE + buf as bufobj: Py_buffer + / + +Start overlapped receive. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_ReadFileInto_impl(OverlappedObject *self, + HANDLE handle, Py_buffer *bufobj) +/*[clinic end generated code: output=8754744506023071 input=4f037ba09939e32d]*/ +{ + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T > SIZEOF_LONG + if (bufobj->len > (Py_ssize_t)ULONG_MAX) { + PyErr_SetString(PyExc_ValueError, "buffer too large"); + return NULL; + } +#endif + steal_buffer(&self->user_buffer, bufobj); + + self->type = TYPE_READINTO; + self->handle = handle; + + return do_ReadFile(self, handle, self->user_buffer.buf, + (DWORD)self->user_buffer.len); +} + +static PyObject * +do_WSARecv(OverlappedObject *self, HANDLE handle, + char *bufstart, DWORD buflen, DWORD flags) +{ + DWORD nread; + WSABUF wsabuf; + int ret; + DWORD err; + + wsabuf.buf = bufstart; + wsabuf.len = buflen; + + Py_BEGIN_ALLOW_THREADS + ret = WSARecv((SOCKET)handle, &wsabuf, 1, &nread, &flags, + &self->overlapped, NULL); + Py_END_ALLOW_THREADS + + self->error = err = (ret < 0 ? WSAGetLastError() : ERROR_SUCCESS); + switch (err) { + case ERROR_BROKEN_PIPE: + mark_as_completed(&self->overlapped); + return SetFromWindowsErr(err); + case ERROR_SUCCESS: + case ERROR_MORE_DATA: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + + +/*[clinic input] +_overlapped.Overlapped.WSARecv + + handle: HANDLE + size: DWORD + flags: DWORD = 0 + / + +Start overlapped receive. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WSARecv_impl(OverlappedObject *self, HANDLE handle, + DWORD size, DWORD flags) +/*[clinic end generated code: output=3a5e9c61ff040906 input=8c04e506cc3d741a]*/ +{ + PyObject *buf; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T <= SIZEOF_LONG + size = Py_MIN(size, (DWORD)PY_SSIZE_T_MAX); +#endif + buf = PyBytes_FromStringAndSize(NULL, Py_MAX(size, 1)); + if (buf == NULL) + return NULL; + + self->type = TYPE_READ; + self->handle = handle; + self->allocated_buffer = buf; + + return do_WSARecv(self, handle, PyBytes_AS_STRING(buf), size, flags); +} + +/*[clinic input] +_overlapped.Overlapped.WSARecvInto + + handle: HANDLE + buf as bufobj: Py_buffer + flags: DWORD + / + +Start overlapped receive. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WSARecvInto_impl(OverlappedObject *self, + HANDLE handle, Py_buffer *bufobj, + DWORD flags) +/*[clinic end generated code: output=59ae7688786cf86b input=73e7fa00db633edd]*/ +{ + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T > SIZEOF_LONG + if (bufobj->len > (Py_ssize_t)ULONG_MAX) { + PyErr_SetString(PyExc_ValueError, "buffer too large"); + return NULL; + } +#endif + steal_buffer(&self->user_buffer, bufobj); + + self->type = TYPE_READINTO; + self->handle = handle; + + return do_WSARecv(self, handle, self->user_buffer.buf, + (DWORD)self->user_buffer.len, flags); +} + +/*[clinic input] +_overlapped.Overlapped.WriteFile + + handle: HANDLE + buf as bufobj: Py_buffer + / + +Start overlapped write. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WriteFile_impl(OverlappedObject *self, HANDLE handle, + Py_buffer *bufobj) +/*[clinic end generated code: output=fa5d5880a1bf04b1 input=ac54424c362abfc1]*/ +{ + DWORD written; + BOOL ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T > SIZEOF_LONG + if (bufobj->len > (Py_ssize_t)ULONG_MAX) { + PyErr_SetString(PyExc_ValueError, "buffer too large"); + return NULL; + } +#endif + steal_buffer(&self->user_buffer, bufobj); + + self->type = TYPE_WRITE; + self->handle = handle; + + Py_BEGIN_ALLOW_THREADS + ret = WriteFile(handle, self->user_buffer.buf, + (DWORD)self->user_buffer.len, + &written, &self->overlapped); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : GetLastError(); + switch (err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.WSASend + + handle: HANDLE + buf as bufobj: Py_buffer + flags: DWORD + / + +Start overlapped send. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WSASend_impl(OverlappedObject *self, HANDLE handle, + Py_buffer *bufobj, DWORD flags) +/*[clinic end generated code: output=3baaa6e1f7fe229e input=c4167420ba2f93d8]*/ +{ + DWORD written; + WSABUF wsabuf; + int ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T > SIZEOF_LONG + if (bufobj->len > (Py_ssize_t)ULONG_MAX) { + PyErr_SetString(PyExc_ValueError, "buffer too large"); + return NULL; + } +#endif + steal_buffer(&self->user_buffer, bufobj); + + self->type = TYPE_WRITE; + self->handle = handle; + wsabuf.len = (DWORD)self->user_buffer.len; + wsabuf.buf = self->user_buffer.buf; + + Py_BEGIN_ALLOW_THREADS + ret = WSASend((SOCKET)handle, &wsabuf, 1, &written, flags, + &self->overlapped, NULL); + Py_END_ALLOW_THREADS + + self->error = err = (ret < 0 ? WSAGetLastError() : ERROR_SUCCESS); + switch (err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.AcceptEx + + listen_handle as ListenSocket: HANDLE + accept_handle as AcceptSocket: HANDLE + / + +Start overlapped wait for client to connect. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_AcceptEx_impl(OverlappedObject *self, + HANDLE ListenSocket, + HANDLE AcceptSocket) +/*[clinic end generated code: output=9a7381d4232af889 input=b83473224fc3a1c5]*/ +{ + DWORD BytesReceived; + DWORD size; + PyObject *buf; + BOOL ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + + size = sizeof(struct sockaddr_in6) + 16; + buf = PyBytes_FromStringAndSize(NULL, size*2); + if (!buf) + return NULL; + + self->type = TYPE_ACCEPT; + self->handle = ListenSocket; + self->allocated_buffer = buf; + + Py_BEGIN_ALLOW_THREADS + ret = Py_AcceptEx((SOCKET)ListenSocket, (SOCKET)AcceptSocket, + PyBytes_AS_STRING(buf), 0, size, size, &BytesReceived, + &self->overlapped); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError(); + switch (err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + + +static int +parse_address(PyObject *obj, SOCKADDR *Address, int Length) +{ + PyObject *Host_obj; + wchar_t *Host; + unsigned short Port; + unsigned long FlowInfo; + unsigned long ScopeId; + + memset(Address, 0, Length); + + switch (PyTuple_GET_SIZE(obj)) { + case 2: { + if (!PyArg_ParseTuple(obj, "UH", &Host_obj, &Port)) { + return -1; + } + Host = PyUnicode_AsWideCharString(Host_obj, NULL); + if (Host == NULL) { + return -1; + } + Address->sa_family = AF_INET; + if (WSAStringToAddressW(Host, AF_INET, NULL, Address, &Length) < 0) { + SetFromWindowsErr(WSAGetLastError()); + Length = -1; + } + else { + ((SOCKADDR_IN*)Address)->sin_port = htons(Port); + } + PyMem_Free(Host); + return Length; + } + case 4: { + if (!PyArg_ParseTuple(obj, + "UHkk;ConnectEx(): illegal address_as_bytes argument", + &Host_obj, &Port, &FlowInfo, &ScopeId)) + { + return -1; + } + Host = PyUnicode_AsWideCharString(Host_obj, NULL); + if (Host == NULL) { + return -1; + } + Address->sa_family = AF_INET6; + if (WSAStringToAddressW(Host, AF_INET6, NULL, Address, &Length) < 0) { + SetFromWindowsErr(WSAGetLastError()); + Length = -1; + } + else { + ((SOCKADDR_IN6*)Address)->sin6_port = htons(Port); + ((SOCKADDR_IN6*)Address)->sin6_flowinfo = FlowInfo; + ((SOCKADDR_IN6*)Address)->sin6_scope_id = ScopeId; + } + PyMem_Free(Host); + return Length; + } + default: + PyErr_SetString(PyExc_ValueError, "illegal address_as_bytes argument"); + return -1; + } +} + +/*[clinic input] +_overlapped.Overlapped.ConnectEx + + client_handle as ConnectSocket: HANDLE + address_as_bytes as AddressObj: object(subclass_of='&PyTuple_Type') + / + +Start overlapped connect. + +client_handle should be unbound. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_ConnectEx_impl(OverlappedObject *self, + HANDLE ConnectSocket, + PyObject *AddressObj) +/*[clinic end generated code: output=5aebbbdb4f022833 input=d6bbd2d84b156fc1]*/ +{ + char AddressBuf[sizeof(struct sockaddr_in6)]; + SOCKADDR *Address = (SOCKADDR*)AddressBuf; + int Length; + BOOL ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + + Length = sizeof(AddressBuf); + Length = parse_address(AddressObj, Address, Length); + if (Length < 0) + return NULL; + + self->type = TYPE_CONNECT; + self->handle = ConnectSocket; + + Py_BEGIN_ALLOW_THREADS + ret = Py_ConnectEx((SOCKET)ConnectSocket, Address, Length, + NULL, 0, NULL, &self->overlapped); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError(); + switch (err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.DisconnectEx + + handle as Socket: HANDLE + flags: DWORD + / + +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_DisconnectEx_impl(OverlappedObject *self, + HANDLE Socket, DWORD flags) +/*[clinic end generated code: output=8d64ddb8c93c2126 input=680845cdcdf820eb]*/ +{ + BOOL ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + + self->type = TYPE_DISCONNECT; + self->handle = Socket; + + Py_BEGIN_ALLOW_THREADS + ret = Py_DisconnectEx((SOCKET)Socket, &self->overlapped, flags, 0); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError(); + switch (err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.TransmitFile + + socket as Socket: HANDLE + file as File: HANDLE + offset: DWORD + offset_high: DWORD + count_to_write: DWORD + count_per_send: DWORD + flags: DWORD + / + +Transmit file data over a connected socket. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_TransmitFile_impl(OverlappedObject *self, + HANDLE Socket, HANDLE File, + DWORD offset, DWORD offset_high, + DWORD count_to_write, + DWORD count_per_send, DWORD flags) +/*[clinic end generated code: output=03f3ca5512e678fd input=7e6f97b391f60e8c]*/ +{ + BOOL ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + + self->type = TYPE_TRANSMIT_FILE; + self->handle = Socket; + self->overlapped.Offset = offset; + self->overlapped.OffsetHigh = offset_high; + + Py_BEGIN_ALLOW_THREADS + ret = Py_TransmitFile((SOCKET)Socket, File, count_to_write, + count_per_send, &self->overlapped, NULL, flags); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError(); + switch (err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.ConnectNamedPipe + + handle as Pipe: HANDLE + / + +Start overlapped wait for a client to connect. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_ConnectNamedPipe_impl(OverlappedObject *self, + HANDLE Pipe) +/*[clinic end generated code: output=3e69adfe55818abe input=8b0d4cef8a72f7bc]*/ +{ + BOOL ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + + self->type = TYPE_CONNECT_NAMED_PIPE; + self->handle = Pipe; + + Py_BEGIN_ALLOW_THREADS + ret = ConnectNamedPipe(Pipe, &self->overlapped); + Py_END_ALLOW_THREADS + + self->error = err = ret ? ERROR_SUCCESS : GetLastError(); + switch (err) { + case ERROR_PIPE_CONNECTED: + mark_as_completed(&self->overlapped); + Py_RETURN_TRUE; + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_FALSE; + default: + Overlapped_clear(self); + return SetFromWindowsErr(err); + } +} + +/*[clinic input] +_overlapped.Overlapped.ConnectPipe + + addr as Address: Py_UNICODE + / + +Connect to the pipe for asynchronous I/O (overlapped). +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_ConnectPipe_impl(OverlappedObject *self, + const Py_UNICODE *Address) +/*[clinic end generated code: output=3cc9661667d459d4 input=167c06a274efcefc]*/ +{ + HANDLE PipeHandle; + + Py_BEGIN_ALLOW_THREADS + PipeHandle = CreateFileW(Address, + GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + Py_END_ALLOW_THREADS + + if (PipeHandle == INVALID_HANDLE_VALUE) + return SetFromWindowsErr(0); + return Py_BuildValue(F_HANDLE, PipeHandle); +} + +static PyObject* +Overlapped_getaddress(OverlappedObject *self) +{ + return PyLong_FromVoidPtr(&self->overlapped); +} + +static PyObject* +Overlapped_getpending(OverlappedObject *self) +{ + return PyBool_FromLong(!HasOverlappedIoCompleted(&self->overlapped) && + self->type != TYPE_NOT_STARTED); +} + +static int +Overlapped_traverse(OverlappedObject *self, visitproc visit, void *arg) +{ + switch (self->type) { + case TYPE_READ: + case TYPE_ACCEPT: + Py_VISIT(self->allocated_buffer); + break; + case TYPE_WRITE: + case TYPE_WRITE_TO: + case TYPE_READINTO: + if (self->user_buffer.obj) { + Py_VISIT(&self->user_buffer.obj); + } + break; + case TYPE_READ_FROM: + Py_VISIT(self->read_from.result); + Py_VISIT(self->read_from.allocated_buffer); + break; + case TYPE_READ_FROM_INTO: + Py_VISIT(self->read_from_into.result); + if (self->read_from_into.user_buffer.obj) { + Py_VISIT(&self->read_from_into.user_buffer.obj); + } + break; + } + return 0; +} + +// UDP functions + +/* + * Note: WSAConnect does not support Overlapped I/O so this function should + * _only_ be used for connectionless sockets (UDP). + */ + +/*[clinic input] +_overlapped.WSAConnect + + client_handle as ConnectSocket: HANDLE + address_as_bytes as AddressObj: object(subclass_of='&PyTuple_Type') + / + +Bind a remote address to a connectionless (UDP) socket. +[clinic start generated code]*/ + +static PyObject * +_overlapped_WSAConnect_impl(PyObject *module, HANDLE ConnectSocket, + PyObject *AddressObj) +/*[clinic end generated code: output=ea0b4391e94dad63 input=7cf65313d49c015a]*/ +{ + char AddressBuf[sizeof(struct sockaddr_in6)]; + SOCKADDR *Address = (SOCKADDR*)AddressBuf; + int Length; + int err; + + Length = sizeof(AddressBuf); + Length = parse_address(AddressObj, Address, Length); + if (Length < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + // WSAConnect does not support overlapped I/O so this call will + // successfully complete immediately. + err = WSAConnect((SOCKET)ConnectSocket, Address, Length, + NULL, NULL, NULL, NULL); + Py_END_ALLOW_THREADS + + if (err == 0) { + Py_RETURN_NONE; + } + else { + return SetFromWindowsErr(WSAGetLastError()); + } +} + +/*[clinic input] +_overlapped.Overlapped.WSASendTo + + handle: HANDLE + buf as bufobj: Py_buffer + flags: DWORD + address_as_bytes as AddressObj: object(subclass_of='&PyTuple_Type') + / + +Start overlapped sendto over a connectionless (UDP) socket. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WSASendTo_impl(OverlappedObject *self, HANDLE handle, + Py_buffer *bufobj, DWORD flags, + PyObject *AddressObj) +/*[clinic end generated code: output=3cdedc4cfaeb70cd input=31f44cd4ab92fc33]*/ +{ + char AddressBuf[sizeof(struct sockaddr_in6)]; + SOCKADDR *Address = (SOCKADDR*)AddressBuf; + int AddressLength; + DWORD written; + WSABUF wsabuf; + int ret; + DWORD err; + + // Parse the "to" address + AddressLength = sizeof(AddressBuf); + AddressLength = parse_address(AddressObj, Address, AddressLength); + if (AddressLength < 0) { + return NULL; + } + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T > SIZEOF_LONG + if (bufobj->len > (Py_ssize_t)ULONG_MAX) { + PyErr_SetString(PyExc_ValueError, "buffer too large"); + return NULL; + } +#endif + steal_buffer(&self->user_buffer, bufobj); + + self->type = TYPE_WRITE_TO; + self->handle = handle; + wsabuf.len = (DWORD)self->user_buffer.len; + wsabuf.buf = self->user_buffer.buf; + + Py_BEGIN_ALLOW_THREADS + ret = WSASendTo((SOCKET)handle, &wsabuf, 1, &written, flags, + Address, AddressLength, &self->overlapped, NULL); + Py_END_ALLOW_THREADS + + self->error = err = (ret == SOCKET_ERROR ? WSAGetLastError() : + ERROR_SUCCESS); + + switch(err) { + case ERROR_SUCCESS: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + self->type = TYPE_NOT_STARTED; + return SetFromWindowsErr(err); + } +} + + + +PyDoc_STRVAR( + Overlapped_WSARecvFrom_doc, + "RecvFile(handle, size, flags) -> Overlapped[(message, (host, port))]\n\n" + "Start overlapped receive"); + +/*[clinic input] +_overlapped.Overlapped.WSARecvFrom + + handle: HANDLE + size: DWORD + flags: DWORD = 0 + / + +Start overlapped receive. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WSARecvFrom_impl(OverlappedObject *self, + HANDLE handle, DWORD size, + DWORD flags) +/*[clinic end generated code: output=13832a2025b86860 input=1b2663fa130e0286]*/ +{ + PyObject *buf; + DWORD nread; + WSABUF wsabuf; + int ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T <= SIZEOF_LONG + size = Py_MIN(size, (DWORD)PY_SSIZE_T_MAX); +#endif + buf = PyBytes_FromStringAndSize(NULL, Py_MAX(size, 1)); + if (buf == NULL) { + return NULL; + } + + wsabuf.buf = PyBytes_AS_STRING(buf); + wsabuf.len = size; + + self->type = TYPE_READ_FROM; + self->handle = handle; + self->read_from.allocated_buffer = buf; + memset(&self->read_from.address, 0, sizeof(self->read_from.address)); + self->read_from.address_length = sizeof(self->read_from.address); + + Py_BEGIN_ALLOW_THREADS + ret = WSARecvFrom((SOCKET)handle, &wsabuf, 1, &nread, &flags, + (SOCKADDR*)&self->read_from.address, + &self->read_from.address_length, + &self->overlapped, NULL); + Py_END_ALLOW_THREADS + + self->error = err = (ret < 0 ? WSAGetLastError() : ERROR_SUCCESS); + switch (err) { + case ERROR_BROKEN_PIPE: + mark_as_completed(&self->overlapped); + return SetFromWindowsErr(err); + case ERROR_SUCCESS: + case ERROR_MORE_DATA: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + self->type = TYPE_NOT_STARTED; + return SetFromWindowsErr(err); + } +} + + +/*[clinic input] +_overlapped.Overlapped.WSARecvFromInto + + handle: HANDLE + buf as bufobj: Py_buffer + size: DWORD + flags: DWORD = 0 + / + +Start overlapped receive. +[clinic start generated code]*/ + +static PyObject * +_overlapped_Overlapped_WSARecvFromInto_impl(OverlappedObject *self, + HANDLE handle, Py_buffer *bufobj, + DWORD size, DWORD flags) +/*[clinic end generated code: output=30c7ea171a691757 input=4be4b08d03531e76]*/ +{ + DWORD nread; + WSABUF wsabuf; + int ret; + DWORD err; + + if (self->type != TYPE_NONE) { + PyErr_SetString(PyExc_ValueError, "operation already attempted"); + return NULL; + } + +#if SIZEOF_SIZE_T > SIZEOF_LONG + if (bufobj->len > (Py_ssize_t)ULONG_MAX) { + PyErr_SetString(PyExc_ValueError, "buffer too large"); + return NULL; + } +#endif + + wsabuf.buf = bufobj->buf; + wsabuf.len = size; + + self->type = TYPE_READ_FROM_INTO; + self->handle = handle; + steal_buffer(&self->read_from_into.user_buffer, bufobj); + memset(&self->read_from_into.address, 0, sizeof(self->read_from_into.address)); + self->read_from_into.address_length = sizeof(self->read_from_into.address); + + Py_BEGIN_ALLOW_THREADS + ret = WSARecvFrom((SOCKET)handle, &wsabuf, 1, &nread, &flags, + (SOCKADDR*)&self->read_from_into.address, + &self->read_from_into.address_length, + &self->overlapped, NULL); + Py_END_ALLOW_THREADS + + self->error = err = (ret < 0 ? WSAGetLastError() : ERROR_SUCCESS); + switch (err) { + case ERROR_BROKEN_PIPE: + mark_as_completed(&self->overlapped); + return SetFromWindowsErr(err); + case ERROR_SUCCESS: + case ERROR_MORE_DATA: + case ERROR_IO_PENDING: + Py_RETURN_NONE; + default: + self->type = TYPE_NOT_STARTED; + return SetFromWindowsErr(err); + } +} + + +#include "clinic/overlapped.c.h" + +static PyMethodDef Overlapped_methods[] = { + _OVERLAPPED_OVERLAPPED_GETRESULT_METHODDEF + _OVERLAPPED_OVERLAPPED_CANCEL_METHODDEF + _OVERLAPPED_OVERLAPPED_READFILE_METHODDEF + _OVERLAPPED_OVERLAPPED_READFILEINTO_METHODDEF + _OVERLAPPED_OVERLAPPED_WSARECV_METHODDEF + _OVERLAPPED_OVERLAPPED_WSARECVINTO_METHODDEF + _OVERLAPPED_OVERLAPPED_WSARECVFROM_METHODDEF + _OVERLAPPED_OVERLAPPED_WSARECVFROMINTO_METHODDEF + _OVERLAPPED_OVERLAPPED_WRITEFILE_METHODDEF + _OVERLAPPED_OVERLAPPED_WSASEND_METHODDEF + _OVERLAPPED_OVERLAPPED_ACCEPTEX_METHODDEF + _OVERLAPPED_OVERLAPPED_CONNECTEX_METHODDEF + _OVERLAPPED_OVERLAPPED_DISCONNECTEX_METHODDEF + _OVERLAPPED_OVERLAPPED_TRANSMITFILE_METHODDEF + _OVERLAPPED_OVERLAPPED_CONNECTNAMEDPIPE_METHODDEF + _OVERLAPPED_OVERLAPPED_WSARECVFROM_METHODDEF + _OVERLAPPED_OVERLAPPED_WSASENDTO_METHODDEF + {NULL} +}; + +static PyMemberDef Overlapped_members[] = { + {"error", T_ULONG, + offsetof(OverlappedObject, error), + READONLY, "Error from last operation"}, + {"event", T_HANDLE, + offsetof(OverlappedObject, overlapped) + offsetof(OVERLAPPED, hEvent), + READONLY, "Overlapped event handle"}, + {NULL} +}; + +static PyGetSetDef Overlapped_getsets[] = { + {"address", (getter)Overlapped_getaddress, NULL, + "Address of overlapped structure"}, + {"pending", (getter)Overlapped_getpending, NULL, + "Whether the operation is pending"}, + {NULL}, +}; + +static PyType_Slot overlapped_type_slots[] = { + {Py_tp_dealloc, Overlapped_dealloc}, + {Py_tp_doc, (char *)_overlapped_Overlapped__doc__}, + {Py_tp_traverse, Overlapped_traverse}, + {Py_tp_methods, Overlapped_methods}, + {Py_tp_members, Overlapped_members}, + {Py_tp_getset, Overlapped_getsets}, + {Py_tp_new, _overlapped_Overlapped}, + {0,0} +}; + +static PyType_Spec overlapped_type_spec = { + .name = "_overlapped.Overlapped", + .basicsize = sizeof(OverlappedObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE), + .slots = overlapped_type_slots +}; + +static PyMethodDef overlapped_functions[] = { + _OVERLAPPED_CREATEIOCOMPLETIONPORT_METHODDEF + _OVERLAPPED_GETQUEUEDCOMPLETIONSTATUS_METHODDEF + _OVERLAPPED_POSTQUEUEDCOMPLETIONSTATUS_METHODDEF + _OVERLAPPED_FORMATMESSAGE_METHODDEF + _OVERLAPPED_BINDLOCAL_METHODDEF + _OVERLAPPED_REGISTERWAITWITHQUEUE_METHODDEF + _OVERLAPPED_UNREGISTERWAIT_METHODDEF + _OVERLAPPED_UNREGISTERWAITEX_METHODDEF + _OVERLAPPED_CREATEEVENT_METHODDEF + _OVERLAPPED_SETEVENT_METHODDEF + _OVERLAPPED_RESETEVENT_METHODDEF + _OVERLAPPED_OVERLAPPED_CONNECTPIPE_METHODDEF + _OVERLAPPED_WSACONNECT_METHODDEF + {NULL} +}; + +#define WINAPI_CONSTANT(fmt, con) \ + do { \ + PyObject *value = Py_BuildValue(fmt, con); \ + if (value == NULL) { \ + return -1; \ + } \ + if (PyModule_AddObject(module, #con, value) < 0 ) { \ + Py_DECREF(value); \ + return -1; \ + } \ + } while (0) + +static int +overlapped_exec(PyObject *module) +{ + /* Ensure WSAStartup() called before initializing function pointers */ + PyObject *socket_module = PyImport_ImportModule("_socket"); + if (!socket_module) { + return -1; + } + + Py_DECREF(socket_module); + + if (initialize_function_pointers() < 0) { + return -1; + } + + PyTypeObject *overlapped_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &overlapped_type_spec, NULL); + if (overlapped_type == NULL) { + return -1; + } + + int rc = PyModule_AddType(module, overlapped_type); + Py_DECREF(overlapped_type); + if (rc < 0) { + return -1; + } + + WINAPI_CONSTANT(F_DWORD, ERROR_IO_PENDING); + WINAPI_CONSTANT(F_DWORD, ERROR_NETNAME_DELETED); + WINAPI_CONSTANT(F_DWORD, ERROR_OPERATION_ABORTED); + WINAPI_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT); + WINAPI_CONSTANT(F_DWORD, ERROR_PIPE_BUSY); + WINAPI_CONSTANT(F_DWORD, INFINITE); + WINAPI_CONSTANT(F_HANDLE, INVALID_HANDLE_VALUE); + WINAPI_CONSTANT(F_HANDLE, NULL); + WINAPI_CONSTANT(F_DWORD, SO_UPDATE_ACCEPT_CONTEXT); + WINAPI_CONSTANT(F_DWORD, SO_UPDATE_CONNECT_CONTEXT); + WINAPI_CONSTANT(F_DWORD, TF_REUSE_SOCKET); + + return 0; +} + +static PyModuleDef_Slot overlapped_slots[] = { + {Py_mod_exec, overlapped_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL} +}; + +static struct PyModuleDef overlapped_module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "_overlapped", + .m_methods = overlapped_functions, + .m_slots = overlapped_slots, +}; + +PyMODINIT_FUNC +PyInit__overlapped(void) +{ + return PyModuleDef_Init(&overlapped_module); +} |