aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/Makefile.am.tpl5
-rw-r--r--apps/poll.c334
-rw-r--r--configure.ac3
-rw-r--r--include/poll.h56
-rwxr-xr-xupdate.sh2
5 files changed, 398 insertions, 2 deletions
diff --git a/apps/Makefile.am.tpl b/apps/Makefile.am.tpl
index deae953..16b3d8b 100644
--- a/apps/Makefile.am.tpl
+++ b/apps/Makefile.am.tpl
@@ -14,3 +14,8 @@ if !HAVE_STRTONUM
14openssl_SOURCES += strtonum.c 14openssl_SOURCES += strtonum.c
15endif 15endif
16 16
17if !HAVE_POLL
18if HOST_WIN
19openssl_SOURCES += poll.c
20endif
21endif
diff --git a/apps/poll.c b/apps/poll.c
new file mode 100644
index 0000000..bf30ccf
--- /dev/null
+++ b/apps/poll.c
@@ -0,0 +1,334 @@
1/*
2 * Public domain
3 *
4 * poll(2) emulation for Windows
5 *
6 * This emulates just-enough poll functionality on Windows to work in the
7 * context of the openssl(1) program. This is not a replacement for
8 * POSIX.1-2001 poll(2), though it may come closer than I care to admit.
9 *
10 * Dongsheng Song <dongsheng.song@gmail.com>
11 * Brent Cook <bcook@openbsd.org>
12 */
13
14#include <conio.h>
15#include <errno.h>
16#include <io.h>
17#include <poll.h>
18#include <ws2tcpip.h>
19
20static int
21conn_is_closed(int fd)
22{
23 char buf[1];
24 int ret = recv(fd, buf, 1, MSG_PEEK);
25 if (ret == -1) {
26 switch (WSAGetLastError()) {
27 case WSAECONNABORTED:
28 case WSAECONNRESET:
29 case WSAENETRESET:
30 case WSAESHUTDOWN:
31 return 1;
32 }
33 }
34 return 0;
35}
36
37static int
38conn_has_oob_data(int fd)
39{
40 char buf[1];
41 return (recv(fd, buf, 1, MSG_PEEK | MSG_OOB) == 1);
42}
43
44static int
45is_socket(int fd)
46{
47 WSANETWORKEVENTS events;
48 return (WSAEnumNetworkEvents((SOCKET)fd, NULL, &events) == 0);
49}
50
51static int
52compute_select_revents(int fd, short events,
53 fd_set *rfds, fd_set *wfds, fd_set *efds)
54{
55 int rc = 0;
56
57 if ((events & (POLLIN | POLLRDNORM | POLLRDBAND)) &&
58 FD_ISSET(fd, rfds)) {
59 if (conn_is_closed(fd))
60 rc |= POLLHUP;
61 else
62 rc |= POLLIN | POLLRDNORM;
63 }
64
65 if ((events & (POLLOUT | POLLWRNORM | POLLWRBAND)) &&
66 FD_ISSET(fd, wfds))
67 rc |= POLLOUT;
68
69 if (FD_ISSET(fd, efds)) {
70 if (conn_is_closed(fd))
71 rc |= POLLHUP;
72 else if (conn_has_oob_data(fd))
73 rc |= POLLRDBAND | POLLPRI;
74 }
75
76 return rc;
77}
78
79static int
80compute_wait_revents(HANDLE h, short events, int object, int wait_rc)
81{
82 int rc = 0;
83 INPUT_RECORD record;
84 DWORD num_read;
85
86 /*
87 * Assume we can always write to file handles (probably a bad
88 * assumption but works for now, at least it doesn't block).
89 */
90 if (events & (POLLOUT | POLLWRNORM))
91 rc |= POLLOUT;
92
93 /*
94 * Check if this handle was signaled by WaitForMultipleObjects
95 */
96 if (wait_rc >= WAIT_OBJECT_0 && (object == (wait_rc - WAIT_OBJECT_0))
97 && (events & (POLLIN | POLLRDNORM))) {
98
99 /*
100 * Check if this file is stdin, and if so, if it is a console.
101 */
102 if (h == GetStdHandle(STD_INPUT_HANDLE) &&
103 PeekConsoleInput(h, &record, 1, &num_read) == 1) {
104
105 /*
106 * Handle the input console buffer differently,
107 * since it can signal on other events like
108 * window and mouse, but read can still block.
109 */
110 if (record.EventType == KEY_EVENT &&
111 record.Event.KeyEvent.bKeyDown) {
112 rc |= POLLIN;
113 } else {
114 /*
115 * Flush non-character events from the
116 * console buffer.
117 */
118 ReadConsoleInput(h, &record, 1, &num_read);
119 }
120 } else {
121 rc |= POLLIN;
122 }
123 }
124
125 return rc;
126}
127
128static int
129wsa_select_errno(int err)
130{
131 switch (err) {
132 case WSAEINTR:
133 case WSAEINPROGRESS:
134 errno = EINTR;
135 break;
136 case WSAEFAULT:
137 /*
138 * Windows uses WSAEFAULT for both resource allocation failures
139 * and arguments not being contained in the user's address
140 * space. So, we have to choose EFAULT or ENOMEM.
141 */
142 errno = EFAULT;
143 break;
144 case WSAEINVAL:
145 errno = EINVAL;
146 break;
147 case WSANOTINITIALISED:
148 errno = EPERM;
149 break;
150 case WSAENETDOWN:
151 errno = ENOMEM;
152 break;
153 }
154 return -1;
155}
156
157int
158poll(struct pollfd *pfds, nfds_t nfds, int timeout_ms)
159{
160 nfds_t i;
161 int timespent_ms, looptime_ms;
162
163#define FD_IS_SOCKET (1 << 0)
164 int fd_state[FD_SETSIZE];
165 int num_fds;
166
167 /*
168 * select machinery
169 */
170 fd_set rfds, wfds, efds;
171 int rc;
172 int num_sockets;
173
174 /*
175 * wait machinery
176 */
177 DWORD wait_rc;
178 HANDLE handles[FD_SETSIZE];
179 int num_handles;
180
181 if (pfds == NULL) {
182 errno = EINVAL;
183 return -1;
184 }
185
186 if (nfds <= 0) {
187 return 0;
188 }
189
190 FD_ZERO(&rfds);
191 FD_ZERO(&wfds);
192 FD_ZERO(&efds);
193 num_fds = 0;
194 num_sockets = 0;
195 num_handles = 0;
196
197 for (i = 0; i < nfds; i++) {
198 if ((int)pfds[i].fd < 0) {
199 continue;
200 }
201
202 if (is_socket(pfds[i].fd)) {
203 if (num_sockets >= FD_SETSIZE) {
204 errno = EINVAL;
205 return -1;
206 }
207
208 fd_state[num_fds] = FD_IS_SOCKET;
209
210 FD_SET(pfds[i].fd, &efds);
211
212 if (pfds[i].events &
213 (POLLIN | POLLRDNORM | POLLRDBAND)) {
214 FD_SET(pfds[i].fd, &rfds);
215 }
216
217 if (pfds[i].events &
218 (POLLOUT | POLLWRNORM | POLLWRBAND)) {
219 FD_SET(pfds[i].fd, &wfds);
220 }
221 num_sockets++;
222
223 } else {
224 if (num_handles >= FD_SETSIZE) {
225 errno = EINVAL;
226 return -1;
227 }
228
229 handles[num_handles++] =
230 (HANDLE)_get_osfhandle(pfds[i].fd);
231 }
232
233 num_fds++;
234 }
235
236 /*
237 * Determine if the files, pipes, sockets, consoles, etc. have signaled.
238 *
239 * Do this by alternating a loop between WaitForMultipleObjects for
240 * non-sockets and and select for sockets.
241 *
242 * I tried to implement this all in terms of WaitForMultipleObjects
243 * with a select-based 'poll' of the sockets at the end to get extra
244 * specific socket status.
245 *
246 * However, the cost of setting up an event handle for each socket and
247 * cleaning them up reliably was pretty high. Since the event handle
248 * associated with a socket is also global, creating a new one here
249 * cancels one that may exist externally to this function.
250 *
251 * At any rate, even if global socket event handles were not an issue,
252 * the 'FD_WRITE' status of a socket event handle does not behave in an
253 * expected fashion, being triggered by an edge on a write buffer rather
254 * than simply triggering if there is space available.
255 */
256 timespent_ms = 0;
257 wait_rc = 0;
258
259 if (timeout_ms < 0) {
260 timeout_ms = INFINITE;
261 }
262 looptime_ms = timeout_ms > 100 ? 100 : timeout_ms;
263
264 do {
265 struct timeval tv = {0, looptime_ms * 1000};
266
267 /*
268 * Check if any file handles have signaled
269 */
270 if (num_handles) {
271 wait_rc = WaitForMultipleObjects(num_handles, handles, FALSE, 0);
272 if (wait_rc == WAIT_FAILED) {
273 /*
274 * The documentation for WaitForMultipleObjects
275 * does not specify what values GetLastError
276 * may return here. Rather than enumerate
277 * badness like for wsa_select_errno, assume a
278 * general errno value.
279 */
280 errno = ENOMEM;
281 return 0;
282 }
283 }
284
285 /*
286 * If we signaled on a file handle, don't wait on the sockets.
287 */
288 if (wait_rc >= WAIT_OBJECT_0)
289 tv.tv_usec = 0;
290
291 /*
292 * Check if any sockets have signaled
293 */
294 rc = select(0, &rfds, &wfds, &efds, &tv);
295 if (rc == SOCKET_ERROR) {
296 return wsa_select_errno(WSAGetLastError());
297 }
298
299 if (wait_rc >= WAIT_OBJECT_0 || (num_sockets && rc > 0))
300 break;
301
302 timespent_ms += looptime_ms;
303
304 } while (timespent_ms < timeout_ms);
305
306 rc = 0;
307 num_handles = 0;
308 num_fds = 0;
309 for (i = 0; i < nfds; i++) {
310 pfds[i].revents = 0;
311
312 if ((int)pfds[i].fd < 0)
313 continue;
314
315 if (fd_state[num_fds] & FD_IS_SOCKET) {
316 pfds[i].revents = compute_select_revents(pfds[i].fd,
317 pfds[i].events, &rfds, &wfds, &efds);
318
319 } else {
320 pfds[i].revents = compute_wait_revents(
321 handles[num_handles], pfds[i].events, num_handles,
322 wait_rc);
323 num_handles++;
324 }
325
326 num_fds++;
327
328 if (pfds[i].revents)
329 rc++;
330 }
331
332 return rc;
333}
334
diff --git a/configure.ac b/configure.ac
index dfa59b5..b434190 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,7 +77,7 @@ CFLAGS="$CFLAGS $CLANG_CFLAGS"
77LDFLAGS="$LDFLAGS $CLANG_FLAGS" 77LDFLAGS="$LDFLAGS $CLANG_FLAGS"
78 78
79AC_CHECK_FUNCS([arc4random_buf asprintf explicit_bzero funopen getauxval]) 79AC_CHECK_FUNCS([arc4random_buf asprintf explicit_bzero funopen getauxval])
80AC_CHECK_FUNCS([getentropy issetugid memmem reallocarray]) 80AC_CHECK_FUNCS([getentropy issetugid memmem poll reallocarray])
81AC_CHECK_FUNCS([strlcat strlcpy strndup strnlen strtonum]) 81AC_CHECK_FUNCS([strlcat strlcpy strndup strnlen strtonum])
82AC_CHECK_FUNCS([timingsafe_bcmp timingsafe_memcmp]) 82AC_CHECK_FUNCS([timingsafe_bcmp timingsafe_memcmp])
83 83
@@ -88,6 +88,7 @@ AM_CONDITIONAL([HAVE_EXPLICIT_BZERO], [test "x$ac_cv_func_explicit_bzero" = xyes
88AM_CONDITIONAL([HAVE_GETENTROPY], [test "x$ac_cv_func_getentropy" = xyes]) 88AM_CONDITIONAL([HAVE_GETENTROPY], [test "x$ac_cv_func_getentropy" = xyes])
89AM_CONDITIONAL([HAVE_ISSETUGID], [test "x$ac_cv_func_issetugid" = xyes]) 89AM_CONDITIONAL([HAVE_ISSETUGID], [test "x$ac_cv_func_issetugid" = xyes])
90AM_CONDITIONAL([HAVE_MEMMEM], [test "x$ac_cv_func_memmem" = xyes]) 90AM_CONDITIONAL([HAVE_MEMMEM], [test "x$ac_cv_func_memmem" = xyes])
91AM_CONDITIONAL([HAVE_POLL], [test "x$ac_cv_func_poll" = xyes])
91AM_CONDITIONAL([HAVE_REALLOCARRAY], [test "x$ac_cv_func_reallocarray" = xyes]) 92AM_CONDITIONAL([HAVE_REALLOCARRAY], [test "x$ac_cv_func_reallocarray" = xyes])
92AM_CONDITIONAL([HAVE_STRLCAT], [test "x$ac_cv_func_strlcat" = xyes]) 93AM_CONDITIONAL([HAVE_STRLCAT], [test "x$ac_cv_func_strlcat" = xyes])
93AM_CONDITIONAL([HAVE_STRLCPY], [test "x$ac_cv_func_strlcpy" = xyes]) 94AM_CONDITIONAL([HAVE_STRLCPY], [test "x$ac_cv_func_strlcpy" = xyes])
diff --git a/include/poll.h b/include/poll.h
new file mode 100644
index 0000000..37f32cd
--- /dev/null
+++ b/include/poll.h
@@ -0,0 +1,56 @@
1/*
2 * poll(2) emulation for Windows
3 * Public domain
4 * from Dongsheng Song <dongsheng.song@gmail.com>
5 */
6
7#ifndef LIBCRYPTOCOMPAT_POLL_H
8#define LIBCRYPTOCOMPAT_POLL_H
9
10#ifdef HAVE_POLL
11#include_next <poll.h>
12#else
13
14#include <winsock2.h>
15
16/* Type used for the number of file descriptors. */
17typedef unsigned long int nfds_t;
18
19#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600)
20/* Data structure describing a polling request. */
21struct pollfd {
22 int fd; /* file descriptor */
23 short events; /* requested events */
24 short revents; /* returned events */
25};
26
27/* Event types that can be polled */
28#define POLLIN 0x001 /* There is data to read. */
29#define POLLPRI 0x002 /* There is urgent data to read. */
30#define POLLOUT 0x004 /* Writing now will not block. */
31
32# define POLLRDNORM 0x040 /* Normal data may be read. */
33# define POLLRDBAND 0x080 /* Priority data may be read. */
34# define POLLWRNORM 0x100 /* Writing now will not block. */
35# define POLLWRBAND 0x200 /* Priority data may be written. */
36
37/* Event types always implicitly polled. */
38#define POLLERR 0x008 /* Error condition. */
39#define POLLHUP 0x010 /* Hung up. */
40#define POLLNVAL 0x020 /* Invalid polling request. */
41
42#endif
43
44#ifdef __cplusplus
45extern "C" {
46#endif
47
48int poll(struct pollfd *pfds, nfds_t nfds, int timeout);
49
50#ifdef __cplusplus
51}
52#endif
53
54#endif /* HAVE_POLL */
55
56#endif /* LIBCRYPTOCOMPAT_POLL_H */
diff --git a/update.sh b/update.sh
index cda2766..1038bb3 100755
--- a/update.sh
+++ b/update.sh
@@ -283,7 +283,6 @@ copy_crypto x509v3 "v3_bcons.c v3_bitst.c v3_conf.c v3_extku.c v3_ia5.c v3_lib.c
283 pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c 283 pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c
284 pcy_int.h ext_dat.h" 284 pcy_int.h ext_dat.h"
285 285
286rm -f apps/*.c apps/*.h
287for i in $openssl_cmd_src/*; do 286for i in $openssl_cmd_src/*; do
288 cp $i apps 287 cp $i apps
289done 288done
@@ -444,6 +443,7 @@ crypto_win32_only=(
444# conditional compiles 443# conditional compiles
445$CP $libc_src/stdlib/strtonum.c apps/ 444$CP $libc_src/stdlib/strtonum.c apps/
446apps_excludes=( 445apps_excludes=(
446 poll.c
447 strtonum.c 447 strtonum.c
448 ) 448 )
449apps_posix_only=( 449apps_posix_only=(