1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18 #include "php_network.h"
19
20 /* Win32 select() will only work with sockets, so we roll our own implementation here.
21 * - If you supply only sockets, this simply passes through to winsock select().
22 * - If you supply file handles, there is no way to distinguish between
23 * ready for read/write or OOB, so any set in which the handle is found will
24 * be marked as ready.
25 * - If you supply a mixture of handles and sockets, the system will interleave
26 * calls between select() and WaitForMultipleObjects(). The time slicing may
27 * cause this function call to take up to 100 ms longer than you specified.
28 * - Calling this with NULL sets as a portable way to sleep with sub-second
29 * accuracy is not supported.
30 * */
php_select(php_socket_t max_fd,fd_set * rfds,fd_set * wfds,fd_set * efds,struct timeval * tv)31 PHPAPI int php_select(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv)
32 {
33 ULONGLONG ms_total, limit;
34 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
35 int handle_slot_to_fd[MAXIMUM_WAIT_OBJECTS];
36 int n_handles = 0, i;
37 fd_set sock_read, sock_write, sock_except;
38 fd_set aread, awrite, aexcept;
39 int sock_max_fd = -1;
40 struct timeval tvslice;
41 int retcode;
42
43 /* As max_fd is unsigned, non socket might overflow. */
44 if (max_fd > (php_socket_t)INT_MAX) {
45 return -1;
46 }
47
48 #define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set))
49
50 /* calculate how long we need to wait in milliseconds */
51 if (tv == NULL) {
52 ms_total = INFINITE;
53 } else {
54 ms_total = tv->tv_sec * 1000;
55 ms_total += tv->tv_usec / 1000;
56 }
57
58 FD_ZERO(&sock_read);
59 FD_ZERO(&sock_write);
60 FD_ZERO(&sock_except);
61
62 /* build an array of handles for non-sockets */
63 for (i = 0; (uint32_t)i < max_fd; i++) {
64 if (SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) {
65 handles[n_handles] = (HANDLE)(zend_uintptr_t)_get_osfhandle(i);
66 if (handles[n_handles] == INVALID_HANDLE_VALUE) {
67 /* socket */
68 if (SAFE_FD_ISSET(i, rfds)) {
69 FD_SET((uint32_t)i, &sock_read);
70 }
71 if (SAFE_FD_ISSET(i, wfds)) {
72 FD_SET((uint32_t)i, &sock_write);
73 }
74 if (SAFE_FD_ISSET(i, efds)) {
75 FD_SET((uint32_t)i, &sock_except);
76 }
77 if (i > sock_max_fd) {
78 sock_max_fd = i;
79 }
80 } else {
81 handle_slot_to_fd[n_handles] = i;
82 n_handles++;
83 }
84 }
85 }
86
87 if (n_handles == 0) {
88 /* plain sockets only - let winsock handle the whole thing */
89 return select(-1, rfds, wfds, efds, tv);
90 }
91
92 /* mixture of handles and sockets; lets multiplex between
93 * winsock and waiting on the handles */
94
95 FD_ZERO(&aread);
96 FD_ZERO(&awrite);
97 FD_ZERO(&aexcept);
98
99 limit = GetTickCount64() + ms_total;
100 do {
101 retcode = 0;
102
103 if (sock_max_fd >= 0) {
104 /* overwrite the zero'd sets here; the select call
105 * will clear those that are not active */
106 aread = sock_read;
107 awrite = sock_write;
108 aexcept = sock_except;
109
110 tvslice.tv_sec = 0;
111 tvslice.tv_usec = 100000;
112
113 retcode = select(-1, &aread, &awrite, &aexcept, &tvslice);
114 }
115 if (n_handles > 0) {
116 /* check handles */
117 DWORD wret;
118
119 wret = WaitForMultipleObjects(n_handles, handles, FALSE, retcode > 0 ? 0 : 100);
120
121 if (wret == WAIT_TIMEOUT) {
122 /* set retcode to 0; this is the default.
123 * select() may have set it to something else,
124 * in which case we leave it alone, so this branch
125 * does nothing */
126 ;
127 } else if (wret == WAIT_FAILED) {
128 if (retcode == 0) {
129 retcode = -1;
130 }
131 } else {
132 if (retcode < 0) {
133 retcode = 0;
134 }
135 for (i = 0; i < n_handles; i++) {
136 if (WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) {
137 if (SAFE_FD_ISSET(handle_slot_to_fd[i], rfds)) {
138 FD_SET((uint32_t)handle_slot_to_fd[i], &aread);
139 }
140 if (SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) {
141 FD_SET((uint32_t)handle_slot_to_fd[i], &awrite);
142 }
143 if (SAFE_FD_ISSET(handle_slot_to_fd[i], efds)) {
144 FD_SET((uint32_t)handle_slot_to_fd[i], &aexcept);
145 }
146 retcode++;
147 }
148 }
149 }
150 }
151 } while (retcode == 0 && (ms_total == INFINITE || GetTickCount64() < limit));
152
153 if (rfds) {
154 *rfds = aread;
155 }
156 if (wfds) {
157 *wfds = awrite;
158 }
159 if (efds) {
160 *efds = aexcept;
161 }
162
163 return retcode;
164 }
165