| /* |
| * 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" |
| |
| #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 |
| |
| enum {TYPE_NONE, TYPE_NOT_STARTED, TYPE_READ, TYPE_WRITE, TYPE_ACCEPT, |
| TYPE_CONNECT, TYPE_DISCONNECT, TYPE_CONNECT_NAMED_PIPE, |
| TYPE_WAIT_NAMED_PIPE_AND_CONNECT}; |
| |
| 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 used for reading (optional) */ |
| PyObject *read_buffer; |
| /* Buffer used for writing (optional) */ |
| Py_buffer write_buffer; |
| }; |
| } OverlappedObject; |
| |
| typedef struct { |
| OVERLAPPED *Overlapped; |
| HANDLE IocpHandle; |
| char Address[1]; |
| } WaitNamedPipeAndConnectContext; |
| |
| /* |
| * 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 BOOL (CALLBACK *Py_CancelIoEx)(HANDLE, LPOVERLAPPED) = 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; |
| HINSTANCE hKernel32; |
| SOCKET s; |
| DWORD dwBytes; |
| |
| 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)) |
| { |
| closesocket(s); |
| SetFromWindowsErr(WSAGetLastError()); |
| return -1; |
| } |
| |
| closesocket(s); |
| |
| /* On WinXP we will have Py_CancelIoEx == NULL */ |
| hKernel32 = GetModuleHandle("KERNEL32"); |
| *(FARPROC *)&Py_CancelIoEx = GetProcAddress(hKernel32, "CancelIoEx"); |
| return 0; |
| } |
| |
| /* |
| * Completion port stuff |
| */ |
| |
| PyDoc_STRVAR( |
| CreateIoCompletionPort_doc, |
| "CreateIoCompletionPort(handle, port, key, concurrency) -> port\n\n" |
| "Create a completion port or register a handle with a port."); |
| |
| static PyObject * |
| overlapped_CreateIoCompletionPort(PyObject *self, PyObject *args) |
| { |
| HANDLE FileHandle; |
| HANDLE ExistingCompletionPort; |
| ULONG_PTR CompletionKey; |
| DWORD NumberOfConcurrentThreads; |
| HANDLE ret; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE F_ULONG_PTR F_DWORD, |
| &FileHandle, &ExistingCompletionPort, &CompletionKey, |
| &NumberOfConcurrentThreads)) |
| return NULL; |
| |
| 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); |
| } |
| |
| PyDoc_STRVAR( |
| GetQueuedCompletionStatus_doc, |
| "GetQueuedCompletionStatus(port, msecs) -> (err, bytes, key, address)\n\n" |
| "Get a message from completion port. Wait for up to msecs milliseconds."); |
| |
| static PyObject * |
| overlapped_GetQueuedCompletionStatus(PyObject *self, PyObject *args) |
| { |
| HANDLE CompletionPort = NULL; |
| DWORD NumberOfBytes = 0; |
| ULONG_PTR CompletionKey = 0; |
| OVERLAPPED *Overlapped = NULL; |
| DWORD Milliseconds; |
| DWORD err; |
| BOOL ret; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD, |
| &CompletionPort, &Milliseconds)) |
| return NULL; |
| |
| 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); |
| } |
| |
| PyDoc_STRVAR( |
| PostQueuedCompletionStatus_doc, |
| "PostQueuedCompletionStatus(port, bytes, key, address) -> None\n\n" |
| "Post a message to completion port."); |
| |
| static PyObject * |
| overlapped_PostQueuedCompletionStatus(PyObject *self, PyObject *args) |
| { |
| HANDLE CompletionPort; |
| DWORD NumberOfBytes; |
| ULONG_PTR CompletionKey; |
| OVERLAPPED *Overlapped; |
| BOOL ret; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD F_ULONG_PTR F_POINTER, |
| &CompletionPort, &NumberOfBytes, &CompletionKey, |
| &Overlapped)) |
| return NULL; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = PostQueuedCompletionStatus(CompletionPort, NumberOfBytes, |
| CompletionKey, Overlapped); |
| Py_END_ALLOW_THREADS |
| |
| if (!ret) |
| return SetFromWindowsErr(0); |
| Py_RETURN_NONE; |
| } |
| |
| /* |
| * Bind socket handle to local port without doing slow getaddrinfo() |
| */ |
| |
| PyDoc_STRVAR( |
| BindLocal_doc, |
| "BindLocal(handle, family) -> None\n\n" |
| "Bind a socket handle to an arbitrary local port.\n" |
| "family should AF_INET or AF_INET6.\n"); |
| |
| static PyObject * |
| overlapped_BindLocal(PyObject *self, PyObject *args) |
| { |
| SOCKET Socket; |
| int Family; |
| BOOL ret; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE "i", &Socket, &Family)) |
| return NULL; |
| |
| 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, (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, (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 |
| */ |
| |
| PyDoc_STRVAR( |
| FormatMessage_doc, |
| "FormatMessage(error_code) -> error_message\n\n" |
| "Return error message for an error code."); |
| |
| static PyObject * |
| overlapped_FormatMessage(PyObject *ignore, PyObject *args) |
| { |
| DWORD code, n; |
| WCHAR *lpMsgBuf; |
| PyObject *res; |
| |
| if (!PyArg_ParseTuple(args, F_DWORD, &code)) |
| return NULL; |
| |
| n = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| FORMAT_MESSAGE_FROM_SYSTEM, |
| 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 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 |
| */ |
| |
| PyDoc_STRVAR( |
| Overlapped_doc, |
| "Overlapped object"); |
| |
| static PyObject * |
| Overlapped_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
| { |
| OverlappedObject *self; |
| HANDLE event = INVALID_HANDLE_VALUE; |
| static char *kwlist[] = {"event", NULL}; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, "|" F_HANDLE, kwlist, &event)) |
| return NULL; |
| |
| 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->read_buffer = NULL; |
| memset(&self->overlapped, 0, sizeof(OVERLAPPED)); |
| memset(&self->write_buffer, 0, sizeof(Py_buffer)); |
| if (event) |
| self->overlapped.hEvent = event; |
| return (PyObject *)self; |
| } |
| |
| 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) |
| { |
| if (Py_CancelIoEx && Py_CancelIoEx(self->handle, &self->overlapped)) |
| wait = TRUE; |
| |
| Py_BEGIN_ALLOW_THREADS |
| 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); |
| |
| if (self->write_buffer.obj) |
| PyBuffer_Release(&self->write_buffer); |
| |
| switch (self->type) { |
| case TYPE_READ: |
| case TYPE_ACCEPT: |
| Py_CLEAR(self->read_buffer); |
| } |
| PyObject_Del(self); |
| SetLastError(olderr); |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_cancel_doc, |
| "cancel() -> None\n\n" |
| "Cancel overlapped operation"); |
| |
| static PyObject * |
| Overlapped_cancel(OverlappedObject *self) |
| { |
| 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 |
| if (Py_CancelIoEx) |
| ret = Py_CancelIoEx(self->handle, &self->overlapped); |
| else |
| ret = CancelIo(self->handle); |
| 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; |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_getresult_doc, |
| "getresult(wait=False) -> result\n\n" |
| "Retrieve result of operation. If wait is true then it blocks\n" |
| "until the operation is finished. If wait is false and the\n" |
| "operation is still pending then an error is raised."); |
| |
| static PyObject * |
| Overlapped_getresult(OverlappedObject *self, PyObject *args) |
| { |
| BOOL wait = FALSE; |
| DWORD transferred = 0; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, "|" F_BOOL, &wait)) |
| return NULL; |
| |
| 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->read_buffer != NULL) |
| break; |
| /* fall through */ |
| default: |
| return SetFromWindowsErr(err); |
| } |
| |
| switch (self->type) { |
| case TYPE_READ: |
| assert(PyBytes_CheckExact(self->read_buffer)); |
| if (transferred != PyBytes_GET_SIZE(self->read_buffer) && |
| _PyBytes_Resize(&self->read_buffer, transferred)) |
| return NULL; |
| Py_INCREF(self->read_buffer); |
| return self->read_buffer; |
| default: |
| return PyLong_FromUnsignedLong((unsigned long) transferred); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_ReadFile_doc, |
| "ReadFile(handle, size) -> Overlapped[message]\n\n" |
| "Start overlapped read"); |
| |
| static PyObject * |
| Overlapped_ReadFile(OverlappedObject *self, PyObject *args) |
| { |
| HANDLE handle; |
| DWORD size; |
| DWORD nread; |
| PyObject *buf; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD, &handle, &size)) |
| return NULL; |
| |
| 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->read_buffer = buf; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = ReadFile(handle, PyBytes_AS_STRING(buf), size, &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); |
| Py_RETURN_NONE; |
| case ERROR_SUCCESS: |
| case ERROR_MORE_DATA: |
| case ERROR_IO_PENDING: |
| Py_RETURN_NONE; |
| default: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_WSARecv_doc, |
| "RecvFile(handle, size, flags) -> Overlapped[message]\n\n" |
| "Start overlapped receive"); |
| |
| static PyObject * |
| Overlapped_WSARecv(OverlappedObject *self, PyObject *args) |
| { |
| HANDLE handle; |
| DWORD size; |
| DWORD flags = 0; |
| DWORD nread; |
| PyObject *buf; |
| WSABUF wsabuf; |
| int ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD "|" F_DWORD, |
| &handle, &size, &flags)) |
| return NULL; |
| |
| 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->read_buffer = buf; |
| wsabuf.len = size; |
| wsabuf.buf = PyBytes_AS_STRING(buf); |
| |
| 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); |
| Py_RETURN_NONE; |
| case ERROR_SUCCESS: |
| case ERROR_MORE_DATA: |
| case ERROR_IO_PENDING: |
| Py_RETURN_NONE; |
| default: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_WriteFile_doc, |
| "WriteFile(handle, buf) -> Overlapped[bytes_transferred]\n\n" |
| "Start overlapped write"); |
| |
| static PyObject * |
| Overlapped_WriteFile(OverlappedObject *self, PyObject *args) |
| { |
| HANDLE handle; |
| PyObject *bufobj; |
| DWORD written; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE "O", &handle, &bufobj)) |
| return NULL; |
| |
| if (self->type != TYPE_NONE) { |
| PyErr_SetString(PyExc_ValueError, "operation already attempted"); |
| return NULL; |
| } |
| |
| if (!PyArg_Parse(bufobj, "y*", &self->write_buffer)) |
| return NULL; |
| |
| #if SIZEOF_SIZE_T > SIZEOF_LONG |
| if (self->write_buffer.len > (Py_ssize_t)ULONG_MAX) { |
| PyBuffer_Release(&self->write_buffer); |
| PyErr_SetString(PyExc_ValueError, "buffer to large"); |
| return NULL; |
| } |
| #endif |
| |
| self->type = TYPE_WRITE; |
| self->handle = handle; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = WriteFile(handle, self->write_buffer.buf, |
| (DWORD)self->write_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: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_WSASend_doc, |
| "WSASend(handle, buf, flags) -> Overlapped[bytes_transferred]\n\n" |
| "Start overlapped send"); |
| |
| static PyObject * |
| Overlapped_WSASend(OverlappedObject *self, PyObject *args) |
| { |
| HANDLE handle; |
| PyObject *bufobj; |
| DWORD flags; |
| DWORD written; |
| WSABUF wsabuf; |
| int ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE "O" F_DWORD, |
| &handle, &bufobj, &flags)) |
| return NULL; |
| |
| if (self->type != TYPE_NONE) { |
| PyErr_SetString(PyExc_ValueError, "operation already attempted"); |
| return NULL; |
| } |
| |
| if (!PyArg_Parse(bufobj, "y*", &self->write_buffer)) |
| return NULL; |
| |
| #if SIZEOF_SIZE_T > SIZEOF_LONG |
| if (self->write_buffer.len > (Py_ssize_t)ULONG_MAX) { |
| PyBuffer_Release(&self->write_buffer); |
| PyErr_SetString(PyExc_ValueError, "buffer to large"); |
| return NULL; |
| } |
| #endif |
| |
| self->type = TYPE_WRITE; |
| self->handle = handle; |
| wsabuf.len = (DWORD)self->write_buffer.len; |
| wsabuf.buf = self->write_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: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_AcceptEx_doc, |
| "AcceptEx(listen_handle, accept_handle) -> Overlapped[address_as_bytes]\n\n" |
| "Start overlapped wait for client to connect"); |
| |
| static PyObject * |
| Overlapped_AcceptEx(OverlappedObject *self, PyObject *args) |
| { |
| SOCKET ListenSocket; |
| SOCKET AcceptSocket; |
| DWORD BytesReceived; |
| DWORD size; |
| PyObject *buf; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, |
| &ListenSocket, &AcceptSocket)) |
| return NULL; |
| |
| 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 = (HANDLE)ListenSocket; |
| self->read_buffer = buf; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = Py_AcceptEx(ListenSocket, 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: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| |
| static int |
| parse_address(PyObject *obj, SOCKADDR *Address, int Length) |
| { |
| char *Host; |
| unsigned short Port; |
| unsigned long FlowInfo; |
| unsigned long ScopeId; |
| |
| memset(Address, 0, Length); |
| |
| if (PyArg_ParseTuple(obj, "sH", &Host, &Port)) |
| { |
| Address->sa_family = AF_INET; |
| if (WSAStringToAddressA(Host, AF_INET, NULL, Address, &Length) < 0) { |
| SetFromWindowsErr(WSAGetLastError()); |
| return -1; |
| } |
| ((SOCKADDR_IN*)Address)->sin_port = htons(Port); |
| return Length; |
| } |
| else if (PyArg_ParseTuple(obj, "sHkk", &Host, &Port, &FlowInfo, &ScopeId)) |
| { |
| PyErr_Clear(); |
| Address->sa_family = AF_INET6; |
| if (WSAStringToAddressA(Host, AF_INET6, NULL, Address, &Length) < 0) { |
| SetFromWindowsErr(WSAGetLastError()); |
| return -1; |
| } |
| ((SOCKADDR_IN6*)Address)->sin6_port = htons(Port); |
| ((SOCKADDR_IN6*)Address)->sin6_flowinfo = FlowInfo; |
| ((SOCKADDR_IN6*)Address)->sin6_scope_id = ScopeId; |
| return Length; |
| } |
| |
| return -1; |
| } |
| |
| |
| PyDoc_STRVAR( |
| Overlapped_ConnectEx_doc, |
| "ConnectEx(client_handle, address_as_bytes) -> Overlapped[None]\n\n" |
| "Start overlapped connect. client_handle should be unbound."); |
| |
| static PyObject * |
| Overlapped_ConnectEx(OverlappedObject *self, PyObject *args) |
| { |
| SOCKET ConnectSocket; |
| PyObject *AddressObj; |
| char AddressBuf[sizeof(struct sockaddr_in6)]; |
| SOCKADDR *Address = (SOCKADDR*)AddressBuf; |
| int Length; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE "O", &ConnectSocket, &AddressObj)) |
| return NULL; |
| |
| 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 = (HANDLE)ConnectSocket; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = Py_ConnectEx(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: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_DisconnectEx_doc, |
| "DisconnectEx(handle, flags) -> Overlapped[None]\n\n" |
| "Start overlapped connect. client_handle should be unbound."); |
| |
| static PyObject * |
| Overlapped_DisconnectEx(OverlappedObject *self, PyObject *args) |
| { |
| SOCKET Socket; |
| DWORD flags; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD, &Socket, &flags)) |
| return NULL; |
| |
| if (self->type != TYPE_NONE) { |
| PyErr_SetString(PyExc_ValueError, "operation already attempted"); |
| return NULL; |
| } |
| |
| self->type = TYPE_DISCONNECT; |
| self->handle = (HANDLE)Socket; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = Py_DisconnectEx(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: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_ConnectNamedPipe_doc, |
| "ConnectNamedPipe(handle) -> Overlapped[None]\n\n" |
| "Start overlapped wait for a client to connect."); |
| |
| static PyObject * |
| Overlapped_ConnectNamedPipe(OverlappedObject *self, PyObject *args) |
| { |
| HANDLE Pipe; |
| BOOL ret; |
| DWORD err; |
| |
| if (!PyArg_ParseTuple(args, F_HANDLE, &Pipe)) |
| return NULL; |
| |
| 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_NONE; |
| case ERROR_SUCCESS: |
| case ERROR_IO_PENDING: |
| Py_RETURN_NONE; |
| default: |
| self->type = TYPE_NOT_STARTED; |
| return SetFromWindowsErr(err); |
| } |
| } |
| |
| /* Unfortunately there is no way to do an overlapped connect to a |
| pipe. We instead use WaitNamedPipe() and CreateFile() in a thread |
| pool thread. If a connection succeeds within a time limit (10 |
| seconds) then PostQueuedCompletionStatus() is used to return the |
| pipe handle to the completion port. */ |
| |
| static DWORD WINAPI |
| WaitNamedPipeAndConnectInThread(WaitNamedPipeAndConnectContext *ctx) |
| { |
| HANDLE PipeHandle = INVALID_HANDLE_VALUE; |
| DWORD Start = GetTickCount(); |
| DWORD Deadline = Start + 10*1000; |
| DWORD Error = 0; |
| DWORD Timeout; |
| BOOL Success; |
| |
| for ( ; ; ) { |
| Timeout = Deadline - GetTickCount(); |
| if ((int)Timeout < 0) |
| break; |
| Success = WaitNamedPipe(ctx->Address, Timeout); |
| Error = Success ? ERROR_SUCCESS : GetLastError(); |
| switch (Error) { |
| case ERROR_SUCCESS: |
| PipeHandle = CreateFile(ctx->Address, |
| GENERIC_READ | GENERIC_WRITE, |
| 0, NULL, OPEN_EXISTING, |
| FILE_FLAG_OVERLAPPED, NULL); |
| if (PipeHandle == INVALID_HANDLE_VALUE) |
| continue; |
| break; |
| case ERROR_SEM_TIMEOUT: |
| continue; |
| } |
| break; |
| } |
| if (!PostQueuedCompletionStatus(ctx->IocpHandle, Error, |
| (ULONG_PTR)PipeHandle, ctx->Overlapped)) |
| CloseHandle(PipeHandle); |
| free(ctx); |
| return 0; |
| } |
| |
| PyDoc_STRVAR( |
| Overlapped_WaitNamedPipeAndConnect_doc, |
| "WaitNamedPipeAndConnect(addr, iocp_handle) -> Overlapped[pipe_handle]\n\n" |
| "Start overlapped connection to address, notifying iocp_handle when\n" |
| "finished"); |
| |
| static PyObject * |
| Overlapped_WaitNamedPipeAndConnect(OverlappedObject *self, PyObject *args) |
| { |
| char *Address; |
| Py_ssize_t AddressLength; |
| HANDLE IocpHandle; |
| OVERLAPPED Overlapped; |
| BOOL ret; |
| DWORD err; |
| WaitNamedPipeAndConnectContext *ctx; |
| Py_ssize_t ContextLength; |
| |
| if (!PyArg_ParseTuple(args, "s#" F_HANDLE F_POINTER, |
| &Address, &AddressLength, &IocpHandle, &Overlapped)) |
| return NULL; |
| |
| if (self->type != TYPE_NONE) { |
| PyErr_SetString(PyExc_ValueError, "operation already attempted"); |
| return NULL; |
| } |
| |
| ContextLength = (AddressLength + |
| offsetof(WaitNamedPipeAndConnectContext, Address)); |
| ctx = calloc(1, ContextLength + 1); |
| if (ctx == NULL) |
| return PyErr_NoMemory(); |
| memcpy(ctx->Address, Address, AddressLength + 1); |
| ctx->Overlapped = &self->overlapped; |
| ctx->IocpHandle = IocpHandle; |
| |
| self->type = TYPE_WAIT_NAMED_PIPE_AND_CONNECT; |
| self->handle = NULL; |
| |
| Py_BEGIN_ALLOW_THREADS |
| ret = QueueUserWorkItem(WaitNamedPipeAndConnectInThread, ctx, |
| WT_EXECUTELONGFUNCTION); |
| Py_END_ALLOW_THREADS |
| |
| mark_as_completed(&self->overlapped); |
| |
| self->error = err = ret ? ERROR_SUCCESS : GetLastError(); |
| if (!ret) |
| return SetFromWindowsErr(err); |
| Py_RETURN_NONE; |
| } |
| |
| 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 PyMethodDef Overlapped_methods[] = { |
| {"getresult", (PyCFunction) Overlapped_getresult, |
| METH_VARARGS, Overlapped_getresult_doc}, |
| {"cancel", (PyCFunction) Overlapped_cancel, |
| METH_NOARGS, Overlapped_cancel_doc}, |
| {"ReadFile", (PyCFunction) Overlapped_ReadFile, |
| METH_VARARGS, Overlapped_ReadFile_doc}, |
| {"WSARecv", (PyCFunction) Overlapped_WSARecv, |
| METH_VARARGS, Overlapped_WSARecv_doc}, |
| {"WriteFile", (PyCFunction) Overlapped_WriteFile, |
| METH_VARARGS, Overlapped_WriteFile_doc}, |
| {"WSASend", (PyCFunction) Overlapped_WSASend, |
| METH_VARARGS, Overlapped_WSASend_doc}, |
| {"AcceptEx", (PyCFunction) Overlapped_AcceptEx, |
| METH_VARARGS, Overlapped_AcceptEx_doc}, |
| {"ConnectEx", (PyCFunction) Overlapped_ConnectEx, |
| METH_VARARGS, Overlapped_ConnectEx_doc}, |
| {"DisconnectEx", (PyCFunction) Overlapped_DisconnectEx, |
| METH_VARARGS, Overlapped_DisconnectEx_doc}, |
| {"ConnectNamedPipe", (PyCFunction) Overlapped_ConnectNamedPipe, |
| METH_VARARGS, Overlapped_ConnectNamedPipe_doc}, |
| {"WaitNamedPipeAndConnect", |
| (PyCFunction) Overlapped_WaitNamedPipeAndConnect, |
| METH_VARARGS, Overlapped_WaitNamedPipeAndConnect_doc}, |
| {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}, |
| }; |
| |
| PyTypeObject OverlappedType = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| /* tp_name */ "_overlapped.Overlapped", |
| /* tp_basicsize */ sizeof(OverlappedObject), |
| /* tp_itemsize */ 0, |
| /* tp_dealloc */ (destructor) Overlapped_dealloc, |
| /* tp_print */ 0, |
| /* tp_getattr */ 0, |
| /* tp_setattr */ 0, |
| /* tp_reserved */ 0, |
| /* tp_repr */ 0, |
| /* tp_as_number */ 0, |
| /* tp_as_sequence */ 0, |
| /* tp_as_mapping */ 0, |
| /* tp_hash */ 0, |
| /* tp_call */ 0, |
| /* tp_str */ 0, |
| /* tp_getattro */ 0, |
| /* tp_setattro */ 0, |
| /* tp_as_buffer */ 0, |
| /* tp_flags */ Py_TPFLAGS_DEFAULT, |
| /* tp_doc */ "OVERLAPPED structure wrapper", |
| /* tp_traverse */ 0, |
| /* tp_clear */ 0, |
| /* tp_richcompare */ 0, |
| /* tp_weaklistoffset */ 0, |
| /* tp_iter */ 0, |
| /* tp_iternext */ 0, |
| /* tp_methods */ Overlapped_methods, |
| /* tp_members */ Overlapped_members, |
| /* tp_getset */ Overlapped_getsets, |
| /* tp_base */ 0, |
| /* tp_dict */ 0, |
| /* tp_descr_get */ 0, |
| /* tp_descr_set */ 0, |
| /* tp_dictoffset */ 0, |
| /* tp_init */ 0, |
| /* tp_alloc */ 0, |
| /* tp_new */ Overlapped_new, |
| }; |
| |
| static PyMethodDef overlapped_functions[] = { |
| {"CreateIoCompletionPort", overlapped_CreateIoCompletionPort, |
| METH_VARARGS, CreateIoCompletionPort_doc}, |
| {"GetQueuedCompletionStatus", overlapped_GetQueuedCompletionStatus, |
| METH_VARARGS, GetQueuedCompletionStatus_doc}, |
| {"PostQueuedCompletionStatus", overlapped_PostQueuedCompletionStatus, |
| METH_VARARGS, PostQueuedCompletionStatus_doc}, |
| {"FormatMessage", overlapped_FormatMessage, |
| METH_VARARGS, FormatMessage_doc}, |
| {"BindLocal", overlapped_BindLocal, |
| METH_VARARGS, BindLocal_doc}, |
| {NULL} |
| }; |
| |
| static struct PyModuleDef overlapped_module = { |
| PyModuleDef_HEAD_INIT, |
| "_overlapped", |
| NULL, |
| -1, |
| overlapped_functions, |
| NULL, |
| NULL, |
| NULL, |
| NULL |
| }; |
| |
| #define WINAPI_CONSTANT(fmt, con) \ |
| PyDict_SetItemString(d, #con, Py_BuildValue(fmt, con)) |
| |
| PyMODINIT_FUNC |
| PyInit__overlapped(void) |
| { |
| PyObject *m, *d; |
| |
| /* Ensure WSAStartup() called before initializing function pointers */ |
| m = PyImport_ImportModule("_socket"); |
| if (!m) |
| return NULL; |
| Py_DECREF(m); |
| |
| if (initialize_function_pointers() < 0) |
| return NULL; |
| |
| if (PyType_Ready(&OverlappedType) < 0) |
| return NULL; |
| |
| m = PyModule_Create(&overlapped_module); |
| if (PyModule_AddObject(m, "Overlapped", (PyObject *)&OverlappedType) < 0) |
| return NULL; |
| |
| d = PyModule_GetDict(m); |
| |
| WINAPI_CONSTANT(F_DWORD, ERROR_IO_PENDING); |
| WINAPI_CONSTANT(F_DWORD, ERROR_NETNAME_DELETED); |
| WINAPI_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT); |
| 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 m; |
| } |