blob: 6a1d9e4a6d82da80957f76e76329f53272a200fe [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001/*
2 * Support for overlapped IO
3 *
4 * Some code borrowed from Modules/_winapi.c of CPython
5 */
6
7/* XXX check overflow and DWORD <-> Py_ssize_t conversions
8 Check itemsize */
9
10#include "Python.h"
11#include "structmember.h"
12
13#define WINDOWS_LEAN_AND_MEAN
14#include <winsock2.h>
15#include <ws2tcpip.h>
16#include <mswsock.h>
17
18#if defined(MS_WIN32) && !defined(MS_WIN64)
19# define F_POINTER "k"
20# define T_POINTER T_ULONG
21#else
22# define F_POINTER "K"
23# define T_POINTER T_ULONGLONG
24#endif
25
26#define F_HANDLE F_POINTER
27#define F_ULONG_PTR F_POINTER
28#define F_DWORD "k"
29#define F_BOOL "i"
30#define F_UINT "I"
31
32#define T_HANDLE T_POINTER
33
34enum {TYPE_NONE, TYPE_NOT_STARTED, TYPE_READ, TYPE_WRITE, TYPE_ACCEPT,
35 TYPE_CONNECT, TYPE_DISCONNECT, TYPE_CONNECT_NAMED_PIPE,
36 TYPE_WAIT_NAMED_PIPE_AND_CONNECT};
37
38typedef struct {
39 PyObject_HEAD
40 OVERLAPPED overlapped;
41 /* For convenience, we store the file handle too */
42 HANDLE handle;
43 /* Error returned by last method call */
44 DWORD error;
45 /* Type of operation */
46 DWORD type;
47 union {
48 /* Buffer used for reading (optional) */
49 PyObject *read_buffer;
50 /* Buffer used for writing (optional) */
51 Py_buffer write_buffer;
52 };
53} OverlappedObject;
54
55typedef struct {
56 OVERLAPPED *Overlapped;
57 HANDLE IocpHandle;
58 char Address[1];
59} WaitNamedPipeAndConnectContext;
60
61/*
62 * Map Windows error codes to subclasses of OSError
63 */
64
65static PyObject *
66SetFromWindowsErr(DWORD err)
67{
68 PyObject *exception_type;
69
70 if (err == 0)
71 err = GetLastError();
72 switch (err) {
73 case ERROR_CONNECTION_REFUSED:
74 exception_type = PyExc_ConnectionRefusedError;
75 break;
76 case ERROR_CONNECTION_ABORTED:
77 exception_type = PyExc_ConnectionAbortedError;
78 break;
79 default:
80 exception_type = PyExc_OSError;
81 }
82 return PyErr_SetExcFromWindowsErr(exception_type, err);
83}
84
85/*
86 * Some functions should be loaded at runtime
87 */
88
89static LPFN_ACCEPTEX Py_AcceptEx = NULL;
90static LPFN_CONNECTEX Py_ConnectEx = NULL;
91static LPFN_DISCONNECTEX Py_DisconnectEx = NULL;
92static BOOL (CALLBACK *Py_CancelIoEx)(HANDLE, LPOVERLAPPED) = NULL;
93
94#define GET_WSA_POINTER(s, x) \
95 (SOCKET_ERROR != WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, \
96 &Guid##x, sizeof(Guid##x), &Py_##x, \
97 sizeof(Py_##x), &dwBytes, NULL, NULL))
98
99static int
100initialize_function_pointers(void)
101{
102 GUID GuidAcceptEx = WSAID_ACCEPTEX;
103 GUID GuidConnectEx = WSAID_CONNECTEX;
104 GUID GuidDisconnectEx = WSAID_DISCONNECTEX;
105 HINSTANCE hKernel32;
106 SOCKET s;
107 DWORD dwBytes;
108
109 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
110 if (s == INVALID_SOCKET) {
111 SetFromWindowsErr(WSAGetLastError());
112 return -1;
113 }
114
115 if (!GET_WSA_POINTER(s, AcceptEx) ||
116 !GET_WSA_POINTER(s, ConnectEx) ||
117 !GET_WSA_POINTER(s, DisconnectEx))
118 {
119 closesocket(s);
120 SetFromWindowsErr(WSAGetLastError());
121 return -1;
122 }
123
124 closesocket(s);
125
126 /* On WinXP we will have Py_CancelIoEx == NULL */
127 hKernel32 = GetModuleHandle("KERNEL32");
128 *(FARPROC *)&Py_CancelIoEx = GetProcAddress(hKernel32, "CancelIoEx");
129 return 0;
130}
131
132/*
133 * Completion port stuff
134 */
135
136PyDoc_STRVAR(
137 CreateIoCompletionPort_doc,
138 "CreateIoCompletionPort(handle, port, key, concurrency) -> port\n\n"
139 "Create a completion port or register a handle with a port.");
140
141static PyObject *
142overlapped_CreateIoCompletionPort(PyObject *self, PyObject *args)
143{
144 HANDLE FileHandle;
145 HANDLE ExistingCompletionPort;
146 ULONG_PTR CompletionKey;
147 DWORD NumberOfConcurrentThreads;
148 HANDLE ret;
149
150 if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE F_ULONG_PTR F_DWORD,
151 &FileHandle, &ExistingCompletionPort, &CompletionKey,
152 &NumberOfConcurrentThreads))
153 return NULL;
154
155 Py_BEGIN_ALLOW_THREADS
156 ret = CreateIoCompletionPort(FileHandle, ExistingCompletionPort,
157 CompletionKey, NumberOfConcurrentThreads);
158 Py_END_ALLOW_THREADS
159
160 if (ret == NULL)
161 return SetFromWindowsErr(0);
162 return Py_BuildValue(F_HANDLE, ret);
163}
164
165PyDoc_STRVAR(
166 GetQueuedCompletionStatus_doc,
167 "GetQueuedCompletionStatus(port, msecs) -> (err, bytes, key, address)\n\n"
168 "Get a message from completion port. Wait for up to msecs milliseconds.");
169
170static PyObject *
171overlapped_GetQueuedCompletionStatus(PyObject *self, PyObject *args)
172{
173 HANDLE CompletionPort = NULL;
174 DWORD NumberOfBytes = 0;
175 ULONG_PTR CompletionKey = 0;
176 OVERLAPPED *Overlapped = NULL;
177 DWORD Milliseconds;
178 DWORD err;
179 BOOL ret;
180
181 if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD,
182 &CompletionPort, &Milliseconds))
183 return NULL;
184
185 Py_BEGIN_ALLOW_THREADS
186 ret = GetQueuedCompletionStatus(CompletionPort, &NumberOfBytes,
187 &CompletionKey, &Overlapped, Milliseconds);
188 Py_END_ALLOW_THREADS
189
190 err = ret ? ERROR_SUCCESS : GetLastError();
191 if (Overlapped == NULL) {
192 if (err == WAIT_TIMEOUT)
193 Py_RETURN_NONE;
194 else
195 return SetFromWindowsErr(err);
196 }
197 return Py_BuildValue(F_DWORD F_DWORD F_ULONG_PTR F_POINTER,
198 err, NumberOfBytes, CompletionKey, Overlapped);
199}
200
201PyDoc_STRVAR(
202 PostQueuedCompletionStatus_doc,
203 "PostQueuedCompletionStatus(port, bytes, key, address) -> None\n\n"
204 "Post a message to completion port.");
205
206static PyObject *
207overlapped_PostQueuedCompletionStatus(PyObject *self, PyObject *args)
208{
209 HANDLE CompletionPort;
210 DWORD NumberOfBytes;
211 ULONG_PTR CompletionKey;
212 OVERLAPPED *Overlapped;
213 BOOL ret;
214
215 if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD F_ULONG_PTR F_POINTER,
216 &CompletionPort, &NumberOfBytes, &CompletionKey,
217 &Overlapped))
218 return NULL;
219
220 Py_BEGIN_ALLOW_THREADS
221 ret = PostQueuedCompletionStatus(CompletionPort, NumberOfBytes,
222 CompletionKey, Overlapped);
223 Py_END_ALLOW_THREADS
224
225 if (!ret)
226 return SetFromWindowsErr(0);
227 Py_RETURN_NONE;
228}
229
230/*
231 * Bind socket handle to local port without doing slow getaddrinfo()
232 */
233
234PyDoc_STRVAR(
235 BindLocal_doc,
236 "BindLocal(handle, family) -> None\n\n"
237 "Bind a socket handle to an arbitrary local port.\n"
238 "family should AF_INET or AF_INET6.\n");
239
240static PyObject *
241overlapped_BindLocal(PyObject *self, PyObject *args)
242{
243 SOCKET Socket;
244 int Family;
245 BOOL ret;
246
247 if (!PyArg_ParseTuple(args, F_HANDLE "i", &Socket, &Family))
248 return NULL;
249
250 if (Family == AF_INET) {
251 struct sockaddr_in addr;
252 memset(&addr, 0, sizeof(addr));
253 addr.sin_family = AF_INET;
254 addr.sin_port = 0;
255 addr.sin_addr.S_un.S_addr = INADDR_ANY;
256 ret = bind(Socket, (SOCKADDR*)&addr, sizeof(addr)) != SOCKET_ERROR;
257 } else if (Family == AF_INET6) {
258 struct sockaddr_in6 addr;
259 memset(&addr, 0, sizeof(addr));
260 addr.sin6_family = AF_INET6;
261 addr.sin6_port = 0;
262 addr.sin6_addr = in6addr_any;
263 ret = bind(Socket, (SOCKADDR*)&addr, sizeof(addr)) != SOCKET_ERROR;
264 } else {
265 PyErr_SetString(PyExc_ValueError, "expected tuple of length 2 or 4");
266 return NULL;
267 }
268
269 if (!ret)
270 return SetFromWindowsErr(WSAGetLastError());
271 Py_RETURN_NONE;
272}
273
274/*
275 * Windows equivalent of os.strerror() -- compare _ctypes/callproc.c
276 */
277
278PyDoc_STRVAR(
279 FormatMessage_doc,
280 "FormatMessage(error_code) -> error_message\n\n"
281 "Return error message for an error code.");
282
283static PyObject *
284overlapped_FormatMessage(PyObject *ignore, PyObject *args)
285{
286 DWORD code, n;
287 WCHAR *lpMsgBuf;
288 PyObject *res;
289
290 if (!PyArg_ParseTuple(args, F_DWORD, &code))
291 return NULL;
292
293 n = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
294 FORMAT_MESSAGE_FROM_SYSTEM,
295 NULL,
296 code,
297 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
298 (LPWSTR) &lpMsgBuf,
299 0,
300 NULL);
301 if (n) {
302 while (iswspace(lpMsgBuf[n-1]))
303 --n;
304 lpMsgBuf[n] = L'\0';
305 res = Py_BuildValue("u", lpMsgBuf);
306 } else {
307 res = PyUnicode_FromFormat("unknown error code %u", code);
308 }
309 LocalFree(lpMsgBuf);
310 return res;
311}
312
313
314/*
315 * Mark operation as completed - used when reading produces ERROR_BROKEN_PIPE
316 */
317
318static void
319mark_as_completed(OVERLAPPED *ov)
320{
321 ov->Internal = 0;
322 if (ov->hEvent != NULL)
323 SetEvent(ov->hEvent);
324}
325
326/*
327 * A Python object wrapping an OVERLAPPED structure and other useful data
328 * for overlapped I/O
329 */
330
331PyDoc_STRVAR(
332 Overlapped_doc,
333 "Overlapped object");
334
335static PyObject *
336Overlapped_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
337{
338 OverlappedObject *self;
339 HANDLE event = INVALID_HANDLE_VALUE;
340 static char *kwlist[] = {"event", NULL};
341
342 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|" F_HANDLE, kwlist, &event))
343 return NULL;
344
345 if (event == INVALID_HANDLE_VALUE) {
346 event = CreateEvent(NULL, TRUE, FALSE, NULL);
347 if (event == NULL)
348 return SetFromWindowsErr(0);
349 }
350
351 self = PyObject_New(OverlappedObject, type);
352 if (self == NULL) {
353 if (event != NULL)
354 CloseHandle(event);
355 return NULL;
356 }
357
358 self->handle = NULL;
359 self->error = 0;
360 self->type = TYPE_NONE;
361 self->read_buffer = NULL;
362 memset(&self->overlapped, 0, sizeof(OVERLAPPED));
363 memset(&self->write_buffer, 0, sizeof(Py_buffer));
364 if (event)
365 self->overlapped.hEvent = event;
366 return (PyObject *)self;
367}
368
369static void
370Overlapped_dealloc(OverlappedObject *self)
371{
372 DWORD bytes;
373 DWORD olderr = GetLastError();
374 BOOL wait = FALSE;
375 BOOL ret;
376
377 if (!HasOverlappedIoCompleted(&self->overlapped) &&
378 self->type != TYPE_NOT_STARTED)
379 {
380 if (Py_CancelIoEx && Py_CancelIoEx(self->handle, &self->overlapped))
381 wait = TRUE;
382
383 Py_BEGIN_ALLOW_THREADS
384 ret = GetOverlappedResult(self->handle, &self->overlapped,
385 &bytes, wait);
386 Py_END_ALLOW_THREADS
387
388 switch (ret ? ERROR_SUCCESS : GetLastError()) {
389 case ERROR_SUCCESS:
390 case ERROR_NOT_FOUND:
391 case ERROR_OPERATION_ABORTED:
392 break;
393 default:
394 PyErr_Format(
395 PyExc_RuntimeError,
396 "%R still has pending operation at "
397 "deallocation, the process may crash", self);
398 PyErr_WriteUnraisable(NULL);
399 }
400 }
401
402 if (self->overlapped.hEvent != NULL)
403 CloseHandle(self->overlapped.hEvent);
404
405 if (self->write_buffer.obj)
406 PyBuffer_Release(&self->write_buffer);
407
408 switch (self->type) {
409 case TYPE_READ:
410 case TYPE_ACCEPT:
411 Py_CLEAR(self->read_buffer);
412 }
413 PyObject_Del(self);
414 SetLastError(olderr);
415}
416
417PyDoc_STRVAR(
418 Overlapped_cancel_doc,
419 "cancel() -> None\n\n"
420 "Cancel overlapped operation");
421
422static PyObject *
423Overlapped_cancel(OverlappedObject *self)
424{
425 BOOL ret = TRUE;
426
427 if (self->type == TYPE_NOT_STARTED
428 || self->type == TYPE_WAIT_NAMED_PIPE_AND_CONNECT)
429 Py_RETURN_NONE;
430
431 if (!HasOverlappedIoCompleted(&self->overlapped)) {
432 Py_BEGIN_ALLOW_THREADS
433 if (Py_CancelIoEx)
434 ret = Py_CancelIoEx(self->handle, &self->overlapped);
435 else
436 ret = CancelIo(self->handle);
437 Py_END_ALLOW_THREADS
438 }
439
440 /* CancelIoEx returns ERROR_NOT_FOUND if the I/O completed in-between */
441 if (!ret && GetLastError() != ERROR_NOT_FOUND)
442 return SetFromWindowsErr(0);
443 Py_RETURN_NONE;
444}
445
446PyDoc_STRVAR(
447 Overlapped_getresult_doc,
448 "getresult(wait=False) -> result\n\n"
449 "Retrieve result of operation. If wait is true then it blocks\n"
450 "until the operation is finished. If wait is false and the\n"
451 "operation is still pending then an error is raised.");
452
453static PyObject *
454Overlapped_getresult(OverlappedObject *self, PyObject *args)
455{
456 BOOL wait = FALSE;
457 DWORD transferred = 0;
458 BOOL ret;
459 DWORD err;
460
461 if (!PyArg_ParseTuple(args, "|" F_BOOL, &wait))
462 return NULL;
463
464 if (self->type == TYPE_NONE) {
465 PyErr_SetString(PyExc_ValueError, "operation not yet attempted");
466 return NULL;
467 }
468
469 if (self->type == TYPE_NOT_STARTED) {
470 PyErr_SetString(PyExc_ValueError, "operation failed to start");
471 return NULL;
472 }
473
474 Py_BEGIN_ALLOW_THREADS
475 ret = GetOverlappedResult(self->handle, &self->overlapped, &transferred,
476 wait);
477 Py_END_ALLOW_THREADS
478
479 self->error = err = ret ? ERROR_SUCCESS : GetLastError();
480 switch (err) {
481 case ERROR_SUCCESS:
482 case ERROR_MORE_DATA:
483 break;
484 case ERROR_BROKEN_PIPE:
485 if (self->read_buffer != NULL)
486 break;
487 /* fall through */
488 default:
489 return SetFromWindowsErr(err);
490 }
491
492 switch (self->type) {
493 case TYPE_READ:
494 assert(PyBytes_CheckExact(self->read_buffer));
495 if (transferred != PyBytes_GET_SIZE(self->read_buffer) &&
496 _PyBytes_Resize(&self->read_buffer, transferred))
497 return NULL;
498 Py_INCREF(self->read_buffer);
499 return self->read_buffer;
500 default:
501 return PyLong_FromUnsignedLong((unsigned long) transferred);
502 }
503}
504
505PyDoc_STRVAR(
506 Overlapped_ReadFile_doc,
507 "ReadFile(handle, size) -> Overlapped[message]\n\n"
508 "Start overlapped read");
509
510static PyObject *
511Overlapped_ReadFile(OverlappedObject *self, PyObject *args)
512{
513 HANDLE handle;
514 DWORD size;
515 DWORD nread;
516 PyObject *buf;
517 BOOL ret;
518 DWORD err;
519
520 if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD, &handle, &size))
521 return NULL;
522
523 if (self->type != TYPE_NONE) {
524 PyErr_SetString(PyExc_ValueError, "operation already attempted");
525 return NULL;
526 }
527
528#if SIZEOF_SIZE_T <= SIZEOF_LONG
529 size = Py_MIN(size, (DWORD)PY_SSIZE_T_MAX);
530#endif
531 buf = PyBytes_FromStringAndSize(NULL, Py_MAX(size, 1));
532 if (buf == NULL)
533 return NULL;
534
535 self->type = TYPE_READ;
536 self->handle = handle;
537 self->read_buffer = buf;
538
539 Py_BEGIN_ALLOW_THREADS
540 ret = ReadFile(handle, PyBytes_AS_STRING(buf), size, &nread,
541 &self->overlapped);
542 Py_END_ALLOW_THREADS
543
544 self->error = err = ret ? ERROR_SUCCESS : GetLastError();
545 switch (err) {
546 case ERROR_BROKEN_PIPE:
547 mark_as_completed(&self->overlapped);
548 Py_RETURN_NONE;
549 case ERROR_SUCCESS:
550 case ERROR_MORE_DATA:
551 case ERROR_IO_PENDING:
552 Py_RETURN_NONE;
553 default:
554 self->type = TYPE_NOT_STARTED;
555 return SetFromWindowsErr(err);
556 }
557}
558
559PyDoc_STRVAR(
560 Overlapped_WSARecv_doc,
561 "RecvFile(handle, size, flags) -> Overlapped[message]\n\n"
562 "Start overlapped receive");
563
564static PyObject *
565Overlapped_WSARecv(OverlappedObject *self, PyObject *args)
566{
567 HANDLE handle;
568 DWORD size;
569 DWORD flags = 0;
570 DWORD nread;
571 PyObject *buf;
572 WSABUF wsabuf;
573 int ret;
574 DWORD err;
575
576 if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD "|" F_DWORD,
577 &handle, &size, &flags))
578 return NULL;
579
580 if (self->type != TYPE_NONE) {
581 PyErr_SetString(PyExc_ValueError, "operation already attempted");
582 return NULL;
583 }
584
585#if SIZEOF_SIZE_T <= SIZEOF_LONG
586 size = Py_MIN(size, (DWORD)PY_SSIZE_T_MAX);
587#endif
588 buf = PyBytes_FromStringAndSize(NULL, Py_MAX(size, 1));
589 if (buf == NULL)
590 return NULL;
591
592 self->type = TYPE_READ;
593 self->handle = handle;
594 self->read_buffer = buf;
595 wsabuf.len = size;
596 wsabuf.buf = PyBytes_AS_STRING(buf);
597
598 Py_BEGIN_ALLOW_THREADS
599 ret = WSARecv((SOCKET)handle, &wsabuf, 1, &nread, &flags,
600 &self->overlapped, NULL);
601 Py_END_ALLOW_THREADS
602
603 self->error = err = (ret < 0 ? WSAGetLastError() : ERROR_SUCCESS);
604 switch (err) {
605 case ERROR_BROKEN_PIPE:
606 mark_as_completed(&self->overlapped);
607 Py_RETURN_NONE;
608 case ERROR_SUCCESS:
609 case ERROR_MORE_DATA:
610 case ERROR_IO_PENDING:
611 Py_RETURN_NONE;
612 default:
613 self->type = TYPE_NOT_STARTED;
614 return SetFromWindowsErr(err);
615 }
616}
617
618PyDoc_STRVAR(
619 Overlapped_WriteFile_doc,
620 "WriteFile(handle, buf) -> Overlapped[bytes_transferred]\n\n"
621 "Start overlapped write");
622
623static PyObject *
624Overlapped_WriteFile(OverlappedObject *self, PyObject *args)
625{
626 HANDLE handle;
627 PyObject *bufobj;
628 DWORD written;
629 BOOL ret;
630 DWORD err;
631
632 if (!PyArg_ParseTuple(args, F_HANDLE "O", &handle, &bufobj))
633 return NULL;
634
635 if (self->type != TYPE_NONE) {
636 PyErr_SetString(PyExc_ValueError, "operation already attempted");
637 return NULL;
638 }
639
640 if (!PyArg_Parse(bufobj, "y*", &self->write_buffer))
641 return NULL;
642
643#if SIZEOF_SIZE_T > SIZEOF_LONG
644 if (self->write_buffer.len > (Py_ssize_t)ULONG_MAX) {
645 PyBuffer_Release(&self->write_buffer);
646 PyErr_SetString(PyExc_ValueError, "buffer to large");
647 return NULL;
648 }
649#endif
650
651 self->type = TYPE_WRITE;
652 self->handle = handle;
653
654 Py_BEGIN_ALLOW_THREADS
655 ret = WriteFile(handle, self->write_buffer.buf,
656 (DWORD)self->write_buffer.len,
657 &written, &self->overlapped);
658 Py_END_ALLOW_THREADS
659
660 self->error = err = ret ? ERROR_SUCCESS : GetLastError();
661 switch (err) {
662 case ERROR_SUCCESS:
663 case ERROR_IO_PENDING:
664 Py_RETURN_NONE;
665 default:
666 self->type = TYPE_NOT_STARTED;
667 return SetFromWindowsErr(err);
668 }
669}
670
671PyDoc_STRVAR(
672 Overlapped_WSASend_doc,
673 "WSASend(handle, buf, flags) -> Overlapped[bytes_transferred]\n\n"
674 "Start overlapped send");
675
676static PyObject *
677Overlapped_WSASend(OverlappedObject *self, PyObject *args)
678{
679 HANDLE handle;
680 PyObject *bufobj;
681 DWORD flags;
682 DWORD written;
683 WSABUF wsabuf;
684 int ret;
685 DWORD err;
686
687 if (!PyArg_ParseTuple(args, F_HANDLE "O" F_DWORD,
688 &handle, &bufobj, &flags))
689 return NULL;
690
691 if (self->type != TYPE_NONE) {
692 PyErr_SetString(PyExc_ValueError, "operation already attempted");
693 return NULL;
694 }
695
696 if (!PyArg_Parse(bufobj, "y*", &self->write_buffer))
697 return NULL;
698
699#if SIZEOF_SIZE_T > SIZEOF_LONG
700 if (self->write_buffer.len > (Py_ssize_t)ULONG_MAX) {
701 PyBuffer_Release(&self->write_buffer);
702 PyErr_SetString(PyExc_ValueError, "buffer to large");
703 return NULL;
704 }
705#endif
706
707 self->type = TYPE_WRITE;
708 self->handle = handle;
709 wsabuf.len = (DWORD)self->write_buffer.len;
710 wsabuf.buf = self->write_buffer.buf;
711
712 Py_BEGIN_ALLOW_THREADS
713 ret = WSASend((SOCKET)handle, &wsabuf, 1, &written, flags,
714 &self->overlapped, NULL);
715 Py_END_ALLOW_THREADS
716
717 self->error = err = (ret < 0 ? WSAGetLastError() : ERROR_SUCCESS);
718 switch (err) {
719 case ERROR_SUCCESS:
720 case ERROR_IO_PENDING:
721 Py_RETURN_NONE;
722 default:
723 self->type = TYPE_NOT_STARTED;
724 return SetFromWindowsErr(err);
725 }
726}
727
728PyDoc_STRVAR(
729 Overlapped_AcceptEx_doc,
730 "AcceptEx(listen_handle, accept_handle) -> Overlapped[address_as_bytes]\n\n"
731 "Start overlapped wait for client to connect");
732
733static PyObject *
734Overlapped_AcceptEx(OverlappedObject *self, PyObject *args)
735{
736 SOCKET ListenSocket;
737 SOCKET AcceptSocket;
738 DWORD BytesReceived;
739 DWORD size;
740 PyObject *buf;
741 BOOL ret;
742 DWORD err;
743
744 if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE,
745 &ListenSocket, &AcceptSocket))
746 return NULL;
747
748 if (self->type != TYPE_NONE) {
749 PyErr_SetString(PyExc_ValueError, "operation already attempted");
750 return NULL;
751 }
752
753 size = sizeof(struct sockaddr_in6) + 16;
754 buf = PyBytes_FromStringAndSize(NULL, size*2);
755 if (!buf)
756 return NULL;
757
758 self->type = TYPE_ACCEPT;
759 self->handle = (HANDLE)ListenSocket;
760 self->read_buffer = buf;
761
762 Py_BEGIN_ALLOW_THREADS
763 ret = Py_AcceptEx(ListenSocket, AcceptSocket, PyBytes_AS_STRING(buf),
764 0, size, size, &BytesReceived, &self->overlapped);
765 Py_END_ALLOW_THREADS
766
767 self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError();
768 switch (err) {
769 case ERROR_SUCCESS:
770 case ERROR_IO_PENDING:
771 Py_RETURN_NONE;
772 default:
773 self->type = TYPE_NOT_STARTED;
774 return SetFromWindowsErr(err);
775 }
776}
777
778
779static int
780parse_address(PyObject *obj, SOCKADDR *Address, int Length)
781{
782 char *Host;
783 unsigned short Port;
784 unsigned long FlowInfo;
785 unsigned long ScopeId;
786
787 memset(Address, 0, Length);
788
789 if (PyArg_ParseTuple(obj, "sH", &Host, &Port))
790 {
791 Address->sa_family = AF_INET;
792 if (WSAStringToAddressA(Host, AF_INET, NULL, Address, &Length) < 0) {
793 SetFromWindowsErr(WSAGetLastError());
794 return -1;
795 }
796 ((SOCKADDR_IN*)Address)->sin_port = htons(Port);
797 return Length;
798 }
799 else if (PyArg_ParseTuple(obj, "sHkk", &Host, &Port, &FlowInfo, &ScopeId))
800 {
801 PyErr_Clear();
802 Address->sa_family = AF_INET6;
803 if (WSAStringToAddressA(Host, AF_INET6, NULL, Address, &Length) < 0) {
804 SetFromWindowsErr(WSAGetLastError());
805 return -1;
806 }
807 ((SOCKADDR_IN6*)Address)->sin6_port = htons(Port);
808 ((SOCKADDR_IN6*)Address)->sin6_flowinfo = FlowInfo;
809 ((SOCKADDR_IN6*)Address)->sin6_scope_id = ScopeId;
810 return Length;
811 }
812
813 return -1;
814}
815
816
817PyDoc_STRVAR(
818 Overlapped_ConnectEx_doc,
819 "ConnectEx(client_handle, address_as_bytes) -> Overlapped[None]\n\n"
820 "Start overlapped connect. client_handle should be unbound.");
821
822static PyObject *
823Overlapped_ConnectEx(OverlappedObject *self, PyObject *args)
824{
825 SOCKET ConnectSocket;
826 PyObject *AddressObj;
827 char AddressBuf[sizeof(struct sockaddr_in6)];
828 SOCKADDR *Address = (SOCKADDR*)AddressBuf;
829 int Length;
830 BOOL ret;
831 DWORD err;
832
833 if (!PyArg_ParseTuple(args, F_HANDLE "O", &ConnectSocket, &AddressObj))
834 return NULL;
835
836 if (self->type != TYPE_NONE) {
837 PyErr_SetString(PyExc_ValueError, "operation already attempted");
838 return NULL;
839 }
840
841 Length = sizeof(AddressBuf);
842 Length = parse_address(AddressObj, Address, Length);
843 if (Length < 0)
844 return NULL;
845
846 self->type = TYPE_CONNECT;
847 self->handle = (HANDLE)ConnectSocket;
848
849 Py_BEGIN_ALLOW_THREADS
850 ret = Py_ConnectEx(ConnectSocket, Address, Length,
851 NULL, 0, NULL, &self->overlapped);
852 Py_END_ALLOW_THREADS
853
854 self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError();
855 switch (err) {
856 case ERROR_SUCCESS:
857 case ERROR_IO_PENDING:
858 Py_RETURN_NONE;
859 default:
860 self->type = TYPE_NOT_STARTED;
861 return SetFromWindowsErr(err);
862 }
863}
864
865PyDoc_STRVAR(
866 Overlapped_DisconnectEx_doc,
867 "DisconnectEx(handle, flags) -> Overlapped[None]\n\n"
868 "Start overlapped connect. client_handle should be unbound.");
869
870static PyObject *
871Overlapped_DisconnectEx(OverlappedObject *self, PyObject *args)
872{
873 SOCKET Socket;
874 DWORD flags;
875 BOOL ret;
876 DWORD err;
877
878 if (!PyArg_ParseTuple(args, F_HANDLE F_DWORD, &Socket, &flags))
879 return NULL;
880
881 if (self->type != TYPE_NONE) {
882 PyErr_SetString(PyExc_ValueError, "operation already attempted");
883 return NULL;
884 }
885
886 self->type = TYPE_DISCONNECT;
887 self->handle = (HANDLE)Socket;
888
889 Py_BEGIN_ALLOW_THREADS
890 ret = Py_DisconnectEx(Socket, &self->overlapped, flags, 0);
891 Py_END_ALLOW_THREADS
892
893 self->error = err = ret ? ERROR_SUCCESS : WSAGetLastError();
894 switch (err) {
895 case ERROR_SUCCESS:
896 case ERROR_IO_PENDING:
897 Py_RETURN_NONE;
898 default:
899 self->type = TYPE_NOT_STARTED;
900 return SetFromWindowsErr(err);
901 }
902}
903
904PyDoc_STRVAR(
905 Overlapped_ConnectNamedPipe_doc,
906 "ConnectNamedPipe(handle) -> Overlapped[None]\n\n"
907 "Start overlapped wait for a client to connect.");
908
909static PyObject *
910Overlapped_ConnectNamedPipe(OverlappedObject *self, PyObject *args)
911{
912 HANDLE Pipe;
913 BOOL ret;
914 DWORD err;
915
916 if (!PyArg_ParseTuple(args, F_HANDLE, &Pipe))
917 return NULL;
918
919 if (self->type != TYPE_NONE) {
920 PyErr_SetString(PyExc_ValueError, "operation already attempted");
921 return NULL;
922 }
923
924 self->type = TYPE_CONNECT_NAMED_PIPE;
925 self->handle = Pipe;
926
927 Py_BEGIN_ALLOW_THREADS
928 ret = ConnectNamedPipe(Pipe, &self->overlapped);
929 Py_END_ALLOW_THREADS
930
931 self->error = err = ret ? ERROR_SUCCESS : GetLastError();
932 switch (err) {
933 case ERROR_PIPE_CONNECTED:
934 mark_as_completed(&self->overlapped);
935 Py_RETURN_NONE;
936 case ERROR_SUCCESS:
937 case ERROR_IO_PENDING:
938 Py_RETURN_NONE;
939 default:
940 self->type = TYPE_NOT_STARTED;
941 return SetFromWindowsErr(err);
942 }
943}
944
945/* Unfortunately there is no way to do an overlapped connect to a
946 pipe. We instead use WaitNamedPipe() and CreateFile() in a thread
947 pool thread. If a connection succeeds within a time limit (10
948 seconds) then PostQueuedCompletionStatus() is used to return the
949 pipe handle to the completion port. */
950
951static DWORD WINAPI
952WaitNamedPipeAndConnectInThread(WaitNamedPipeAndConnectContext *ctx)
953{
954 HANDLE PipeHandle = INVALID_HANDLE_VALUE;
955 DWORD Start = GetTickCount();
956 DWORD Deadline = Start + 10*1000;
957 DWORD Error = 0;
958 DWORD Timeout;
959 BOOL Success;
960
961 for ( ; ; ) {
962 Timeout = Deadline - GetTickCount();
963 if ((int)Timeout < 0)
964 break;
965 Success = WaitNamedPipe(ctx->Address, Timeout);
966 Error = Success ? ERROR_SUCCESS : GetLastError();
967 switch (Error) {
968 case ERROR_SUCCESS:
969 PipeHandle = CreateFile(ctx->Address,
970 GENERIC_READ | GENERIC_WRITE,
971 0, NULL, OPEN_EXISTING,
972 FILE_FLAG_OVERLAPPED, NULL);
973 if (PipeHandle == INVALID_HANDLE_VALUE)
974 continue;
975 break;
976 case ERROR_SEM_TIMEOUT:
977 continue;
978 }
979 break;
980 }
981 if (!PostQueuedCompletionStatus(ctx->IocpHandle, Error,
982 (ULONG_PTR)PipeHandle, ctx->Overlapped))
983 CloseHandle(PipeHandle);
984 free(ctx);
985 return 0;
986}
987
988PyDoc_STRVAR(
989 Overlapped_WaitNamedPipeAndConnect_doc,
990 "WaitNamedPipeAndConnect(addr, iocp_handle) -> Overlapped[pipe_handle]\n\n"
991 "Start overlapped connection to address, notifying iocp_handle when\n"
992 "finished");
993
994static PyObject *
995Overlapped_WaitNamedPipeAndConnect(OverlappedObject *self, PyObject *args)
996{
997 char *Address;
998 Py_ssize_t AddressLength;
999 HANDLE IocpHandle;
1000 OVERLAPPED Overlapped;
1001 BOOL ret;
1002 DWORD err;
1003 WaitNamedPipeAndConnectContext *ctx;
1004 Py_ssize_t ContextLength;
1005
1006 if (!PyArg_ParseTuple(args, "s#" F_HANDLE F_POINTER,
1007 &Address, &AddressLength, &IocpHandle, &Overlapped))
1008 return NULL;
1009
1010 if (self->type != TYPE_NONE) {
1011 PyErr_SetString(PyExc_ValueError, "operation already attempted");
1012 return NULL;
1013 }
1014
1015 ContextLength = (AddressLength +
1016 offsetof(WaitNamedPipeAndConnectContext, Address));
1017 ctx = calloc(1, ContextLength + 1);
1018 if (ctx == NULL)
1019 return PyErr_NoMemory();
1020 memcpy(ctx->Address, Address, AddressLength + 1);
1021 ctx->Overlapped = &self->overlapped;
1022 ctx->IocpHandle = IocpHandle;
1023
1024 self->type = TYPE_WAIT_NAMED_PIPE_AND_CONNECT;
1025 self->handle = NULL;
1026
1027 Py_BEGIN_ALLOW_THREADS
1028 ret = QueueUserWorkItem(WaitNamedPipeAndConnectInThread, ctx,
1029 WT_EXECUTELONGFUNCTION);
1030 Py_END_ALLOW_THREADS
1031
1032 mark_as_completed(&self->overlapped);
1033
1034 self->error = err = ret ? ERROR_SUCCESS : GetLastError();
1035 if (!ret)
1036 return SetFromWindowsErr(err);
1037 Py_RETURN_NONE;
1038}
1039
1040static PyObject*
1041Overlapped_getaddress(OverlappedObject *self)
1042{
1043 return PyLong_FromVoidPtr(&self->overlapped);
1044}
1045
1046static PyObject*
1047Overlapped_getpending(OverlappedObject *self)
1048{
1049 return PyBool_FromLong(!HasOverlappedIoCompleted(&self->overlapped) &&
1050 self->type != TYPE_NOT_STARTED);
1051}
1052
1053static PyMethodDef Overlapped_methods[] = {
1054 {"getresult", (PyCFunction) Overlapped_getresult,
1055 METH_VARARGS, Overlapped_getresult_doc},
1056 {"cancel", (PyCFunction) Overlapped_cancel,
1057 METH_NOARGS, Overlapped_cancel_doc},
1058 {"ReadFile", (PyCFunction) Overlapped_ReadFile,
1059 METH_VARARGS, Overlapped_ReadFile_doc},
1060 {"WSARecv", (PyCFunction) Overlapped_WSARecv,
1061 METH_VARARGS, Overlapped_WSARecv_doc},
1062 {"WriteFile", (PyCFunction) Overlapped_WriteFile,
1063 METH_VARARGS, Overlapped_WriteFile_doc},
1064 {"WSASend", (PyCFunction) Overlapped_WSASend,
1065 METH_VARARGS, Overlapped_WSASend_doc},
1066 {"AcceptEx", (PyCFunction) Overlapped_AcceptEx,
1067 METH_VARARGS, Overlapped_AcceptEx_doc},
1068 {"ConnectEx", (PyCFunction) Overlapped_ConnectEx,
1069 METH_VARARGS, Overlapped_ConnectEx_doc},
1070 {"DisconnectEx", (PyCFunction) Overlapped_DisconnectEx,
1071 METH_VARARGS, Overlapped_DisconnectEx_doc},
1072 {"ConnectNamedPipe", (PyCFunction) Overlapped_ConnectNamedPipe,
1073 METH_VARARGS, Overlapped_ConnectNamedPipe_doc},
1074 {"WaitNamedPipeAndConnect",
1075 (PyCFunction) Overlapped_WaitNamedPipeAndConnect,
1076 METH_VARARGS, Overlapped_WaitNamedPipeAndConnect_doc},
1077 {NULL}
1078};
1079
1080static PyMemberDef Overlapped_members[] = {
1081 {"error", T_ULONG,
1082 offsetof(OverlappedObject, error),
1083 READONLY, "Error from last operation"},
1084 {"event", T_HANDLE,
1085 offsetof(OverlappedObject, overlapped) + offsetof(OVERLAPPED, hEvent),
1086 READONLY, "Overlapped event handle"},
1087 {NULL}
1088};
1089
1090static PyGetSetDef Overlapped_getsets[] = {
1091 {"address", (getter)Overlapped_getaddress, NULL,
1092 "Address of overlapped structure"},
1093 {"pending", (getter)Overlapped_getpending, NULL,
1094 "Whether the operation is pending"},
1095 {NULL},
1096};
1097
1098PyTypeObject OverlappedType = {
1099 PyVarObject_HEAD_INIT(NULL, 0)
1100 /* tp_name */ "_overlapped.Overlapped",
1101 /* tp_basicsize */ sizeof(OverlappedObject),
1102 /* tp_itemsize */ 0,
1103 /* tp_dealloc */ (destructor) Overlapped_dealloc,
1104 /* tp_print */ 0,
1105 /* tp_getattr */ 0,
1106 /* tp_setattr */ 0,
1107 /* tp_reserved */ 0,
1108 /* tp_repr */ 0,
1109 /* tp_as_number */ 0,
1110 /* tp_as_sequence */ 0,
1111 /* tp_as_mapping */ 0,
1112 /* tp_hash */ 0,
1113 /* tp_call */ 0,
1114 /* tp_str */ 0,
1115 /* tp_getattro */ 0,
1116 /* tp_setattro */ 0,
1117 /* tp_as_buffer */ 0,
1118 /* tp_flags */ Py_TPFLAGS_DEFAULT,
1119 /* tp_doc */ "OVERLAPPED structure wrapper",
1120 /* tp_traverse */ 0,
1121 /* tp_clear */ 0,
1122 /* tp_richcompare */ 0,
1123 /* tp_weaklistoffset */ 0,
1124 /* tp_iter */ 0,
1125 /* tp_iternext */ 0,
1126 /* tp_methods */ Overlapped_methods,
1127 /* tp_members */ Overlapped_members,
1128 /* tp_getset */ Overlapped_getsets,
1129 /* tp_base */ 0,
1130 /* tp_dict */ 0,
1131 /* tp_descr_get */ 0,
1132 /* tp_descr_set */ 0,
1133 /* tp_dictoffset */ 0,
1134 /* tp_init */ 0,
1135 /* tp_alloc */ 0,
1136 /* tp_new */ Overlapped_new,
1137};
1138
1139static PyMethodDef overlapped_functions[] = {
1140 {"CreateIoCompletionPort", overlapped_CreateIoCompletionPort,
1141 METH_VARARGS, CreateIoCompletionPort_doc},
1142 {"GetQueuedCompletionStatus", overlapped_GetQueuedCompletionStatus,
1143 METH_VARARGS, GetQueuedCompletionStatus_doc},
1144 {"PostQueuedCompletionStatus", overlapped_PostQueuedCompletionStatus,
1145 METH_VARARGS, PostQueuedCompletionStatus_doc},
1146 {"FormatMessage", overlapped_FormatMessage,
1147 METH_VARARGS, FormatMessage_doc},
1148 {"BindLocal", overlapped_BindLocal,
1149 METH_VARARGS, BindLocal_doc},
1150 {NULL}
1151};
1152
1153static struct PyModuleDef overlapped_module = {
1154 PyModuleDef_HEAD_INIT,
1155 "_overlapped",
1156 NULL,
1157 -1,
1158 overlapped_functions,
1159 NULL,
1160 NULL,
1161 NULL,
1162 NULL
1163};
1164
1165#define WINAPI_CONSTANT(fmt, con) \
1166 PyDict_SetItemString(d, #con, Py_BuildValue(fmt, con))
1167
1168PyMODINIT_FUNC
1169PyInit__overlapped(void)
1170{
1171 PyObject *m, *d;
1172
1173 /* Ensure WSAStartup() called before initializing function pointers */
1174 m = PyImport_ImportModule("_socket");
1175 if (!m)
1176 return NULL;
1177 Py_DECREF(m);
1178
1179 if (initialize_function_pointers() < 0)
1180 return NULL;
1181
1182 if (PyType_Ready(&OverlappedType) < 0)
1183 return NULL;
1184
1185 m = PyModule_Create(&overlapped_module);
1186 if (PyModule_AddObject(m, "Overlapped", (PyObject *)&OverlappedType) < 0)
1187 return NULL;
1188
1189 d = PyModule_GetDict(m);
1190
1191 WINAPI_CONSTANT(F_DWORD, ERROR_IO_PENDING);
1192 WINAPI_CONSTANT(F_DWORD, ERROR_NETNAME_DELETED);
1193 WINAPI_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT);
1194 WINAPI_CONSTANT(F_DWORD, INFINITE);
1195 WINAPI_CONSTANT(F_HANDLE, INVALID_HANDLE_VALUE);
1196 WINAPI_CONSTANT(F_HANDLE, NULL);
1197 WINAPI_CONSTANT(F_DWORD, SO_UPDATE_ACCEPT_CONTEXT);
1198 WINAPI_CONSTANT(F_DWORD, SO_UPDATE_CONNECT_CONTEXT);
1199 WINAPI_CONSTANT(F_DWORD, TF_REUSE_SOCKET);
1200
1201 return m;
1202}