xref: /openssl/ssl/quic/quic_reactor.c (revision da1c088f)
1 /*
2  * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 #include "internal/quic_reactor.h"
10 #include "internal/common.h"
11 #include "internal/thread_arch.h"
12 
13 /*
14  * Core I/O Reactor Framework
15  * ==========================
16  */
ossl_quic_reactor_init(QUIC_REACTOR * rtor,void (* tick_cb)(QUIC_TICK_RESULT * res,void * arg,uint32_t flags),void * tick_cb_arg,OSSL_TIME initial_tick_deadline)17 void ossl_quic_reactor_init(QUIC_REACTOR *rtor,
18                             void (*tick_cb)(QUIC_TICK_RESULT *res, void *arg,
19                                             uint32_t flags),
20                             void *tick_cb_arg,
21                             OSSL_TIME initial_tick_deadline)
22 {
23     rtor->poll_r.type       = BIO_POLL_DESCRIPTOR_TYPE_NONE;
24     rtor->poll_w.type       = BIO_POLL_DESCRIPTOR_TYPE_NONE;
25     rtor->net_read_desired  = 0;
26     rtor->net_write_desired = 0;
27     rtor->can_poll_r        = 0;
28     rtor->can_poll_w        = 0;
29     rtor->tick_deadline     = initial_tick_deadline;
30 
31     rtor->tick_cb           = tick_cb;
32     rtor->tick_cb_arg       = tick_cb_arg;
33 }
34 
ossl_quic_reactor_set_poll_r(QUIC_REACTOR * rtor,const BIO_POLL_DESCRIPTOR * r)35 void ossl_quic_reactor_set_poll_r(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR *r)
36 {
37     if (r == NULL)
38         rtor->poll_r.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
39     else
40         rtor->poll_r = *r;
41 
42     rtor->can_poll_r
43         = ossl_quic_reactor_can_support_poll_descriptor(rtor, &rtor->poll_r);
44 }
45 
ossl_quic_reactor_set_poll_w(QUIC_REACTOR * rtor,const BIO_POLL_DESCRIPTOR * w)46 void ossl_quic_reactor_set_poll_w(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR *w)
47 {
48     if (w == NULL)
49         rtor->poll_w.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
50     else
51         rtor->poll_w = *w;
52 
53     rtor->can_poll_w
54         = ossl_quic_reactor_can_support_poll_descriptor(rtor, &rtor->poll_w);
55 }
56 
ossl_quic_reactor_get_poll_r(const QUIC_REACTOR * rtor)57 const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_r(const QUIC_REACTOR *rtor)
58 {
59     return &rtor->poll_r;
60 }
61 
ossl_quic_reactor_get_poll_w(const QUIC_REACTOR * rtor)62 const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_w(const QUIC_REACTOR *rtor)
63 {
64     return &rtor->poll_w;
65 }
66 
ossl_quic_reactor_can_support_poll_descriptor(const QUIC_REACTOR * rtor,const BIO_POLL_DESCRIPTOR * d)67 int ossl_quic_reactor_can_support_poll_descriptor(const QUIC_REACTOR *rtor,
68                                                   const BIO_POLL_DESCRIPTOR *d)
69 {
70     return d->type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD;
71 }
72 
ossl_quic_reactor_can_poll_r(const QUIC_REACTOR * rtor)73 int ossl_quic_reactor_can_poll_r(const QUIC_REACTOR *rtor)
74 {
75     return rtor->can_poll_r;
76 }
77 
ossl_quic_reactor_can_poll_w(const QUIC_REACTOR * rtor)78 int ossl_quic_reactor_can_poll_w(const QUIC_REACTOR *rtor)
79 {
80     return rtor->can_poll_w;
81 }
82 
ossl_quic_reactor_net_read_desired(QUIC_REACTOR * rtor)83 int ossl_quic_reactor_net_read_desired(QUIC_REACTOR *rtor)
84 {
85     return rtor->net_read_desired;
86 }
87 
ossl_quic_reactor_net_write_desired(QUIC_REACTOR * rtor)88 int ossl_quic_reactor_net_write_desired(QUIC_REACTOR *rtor)
89 {
90     return rtor->net_write_desired;
91 }
92 
ossl_quic_reactor_get_tick_deadline(QUIC_REACTOR * rtor)93 OSSL_TIME ossl_quic_reactor_get_tick_deadline(QUIC_REACTOR *rtor)
94 {
95     return rtor->tick_deadline;
96 }
97 
ossl_quic_reactor_tick(QUIC_REACTOR * rtor,uint32_t flags)98 int ossl_quic_reactor_tick(QUIC_REACTOR *rtor, uint32_t flags)
99 {
100     QUIC_TICK_RESULT res = {0};
101 
102     /*
103      * Note that the tick callback cannot fail; this is intentional. Arguably it
104      * does not make that much sense for ticking to 'fail' (in the sense of an
105      * explicit error indicated to the user) because ticking is by its nature
106      * best effort. If something fatal happens with a connection we can report
107      * it on the next actual application I/O call.
108      */
109     rtor->tick_cb(&res, rtor->tick_cb_arg, flags);
110 
111     rtor->net_read_desired  = res.net_read_desired;
112     rtor->net_write_desired = res.net_write_desired;
113     rtor->tick_deadline     = res.tick_deadline;
114     return 1;
115 }
116 
117 /*
118  * Blocking I/O Adaptation Layer
119  * =============================
120  */
121 
122 /*
123  * Utility which can be used to poll on up to two FDs. This is designed to
124  * support use of split FDs (e.g. with SSL_set_rfd and SSL_set_wfd where
125  * different FDs are used for read and write).
126  *
127  * Generally use of poll(2) is preferred where available. Windows, however,
128  * hasn't traditionally offered poll(2), only select(2). WSAPoll() was
129  * introduced in Vista but has seemingly been buggy until relatively recent
130  * versions of Windows 10. Moreover we support XP so this is not a suitable
131  * target anyway. However, the traditional issues with select(2) turn out not to
132  * be an issue on Windows; whereas traditional *NIX select(2) uses a bitmap of
133  * FDs (and thus is limited in the magnitude of the FDs expressible), Windows
134  * select(2) is very different. In Windows, socket handles are not allocated
135  * contiguously from zero and thus this bitmap approach was infeasible. Thus in
136  * adapting the Berkeley sockets API to Windows a different approach was taken
137  * whereby the fd_set contains a fixed length array of socket handles and an
138  * integer indicating how many entries are valid; thus Windows select()
139  * ironically is actually much more like *NIX poll(2) than *NIX select(2). In
140  * any case, this means that the relevant limit for Windows select() is the
141  * number of FDs being polled, not the magnitude of those FDs. Since we only
142  * poll for two FDs here, this limit does not concern us.
143  *
144  * Usage: rfd and wfd may be the same or different. Either or both may also be
145  * -1. If rfd_want_read is 1, rfd is polled for readability, and if
146  * wfd_want_write is 1, wfd is polled for writability. Note that since any
147  * passed FD is always polled for error conditions, setting rfd_want_read=0 and
148  * wfd_want_write=0 is not the same as passing -1 for both FDs.
149  *
150  * deadline is a timestamp to return at. If it is ossl_time_infinite(), the call
151  * never times out.
152  *
153  * Returns 0 on error and 1 on success. Timeout expiry is considered a success
154  * condition. We don't elaborate our return values here because the way we are
155  * actually using this doesn't currently care.
156  *
157  * If mutex is non-NULL, it is assumed to be held for write and is unlocked for
158  * the duration of the call.
159  *
160  * Precondition:   mutex is NULL or is held for write (unchecked)
161  * Postcondition:  mutex is NULL or is held for write (unless
162  *                   CRYPTO_THREAD_write_lock fails)
163  */
poll_two_fds(int rfd,int rfd_want_read,int wfd,int wfd_want_write,OSSL_TIME deadline,CRYPTO_MUTEX * mutex)164 static int poll_two_fds(int rfd, int rfd_want_read,
165                         int wfd, int wfd_want_write,
166                         OSSL_TIME deadline,
167                         CRYPTO_MUTEX *mutex)
168 {
169 #if defined(OPENSSL_SYS_WINDOWS) || !defined(POLLIN)
170     fd_set rfd_set, wfd_set, efd_set;
171     OSSL_TIME now, timeout;
172     struct timeval tv, *ptv;
173     int maxfd, pres;
174 
175 # ifndef OPENSSL_SYS_WINDOWS
176     /*
177      * On Windows there is no relevant limit to the magnitude of a fd value (see
178      * above). On *NIX the fd_set uses a bitmap and we must check the limit.
179      */
180     if (rfd >= FD_SETSIZE || wfd >= FD_SETSIZE)
181         return 0;
182 # endif
183 
184     FD_ZERO(&rfd_set);
185     FD_ZERO(&wfd_set);
186     FD_ZERO(&efd_set);
187 
188     if (rfd != -1 && rfd_want_read)
189         openssl_fdset(rfd, &rfd_set);
190     if (wfd != -1 && wfd_want_write)
191         openssl_fdset(wfd, &wfd_set);
192 
193     /* Always check for error conditions. */
194     if (rfd != -1)
195         openssl_fdset(rfd, &efd_set);
196     if (wfd != -1)
197         openssl_fdset(wfd, &efd_set);
198 
199     maxfd = rfd;
200     if (wfd > maxfd)
201         maxfd = wfd;
202 
203     if (!ossl_assert(rfd != -1 || wfd != -1
204                      || !ossl_time_is_infinite(deadline)))
205         /* Do not block forever; should not happen. */
206         return 0;
207 
208 # if defined(OPENSSL_THREADS)
209     if (mutex != NULL)
210         ossl_crypto_mutex_unlock(mutex);
211 # endif
212 
213     do {
214         /*
215          * select expects a timeout, not a deadline, so do the conversion.
216          * Update for each call to ensure the correct value is used if we repeat
217          * due to EINTR.
218          */
219         if (ossl_time_is_infinite(deadline)) {
220             ptv = NULL;
221         } else {
222             now = ossl_time_now();
223             /*
224              * ossl_time_subtract saturates to zero so we don't need to check if
225              * now > deadline.
226              */
227             timeout = ossl_time_subtract(deadline, now);
228             tv      = ossl_time_to_timeval(timeout);
229             ptv     = &tv;
230         }
231 
232         pres = select(maxfd + 1, &rfd_set, &wfd_set, &efd_set, ptv);
233     } while (pres == -1 && get_last_socket_error_is_eintr());
234 
235 # if defined(OPENSSL_THREADS)
236     if (mutex != NULL)
237         ossl_crypto_mutex_lock(mutex);
238 # endif
239 
240     return pres < 0 ? 0 : 1;
241 #else
242     int pres, timeout_ms;
243     OSSL_TIME now, timeout;
244     struct pollfd pfds[2] = {0};
245     size_t npfd = 0;
246 
247     if (rfd == wfd) {
248         pfds[npfd].fd = rfd;
249         pfds[npfd].events = (rfd_want_read  ? POLLIN  : 0)
250                           | (wfd_want_write ? POLLOUT : 0);
251         if (rfd >= 0 && pfds[npfd].events != 0)
252             ++npfd;
253     } else {
254         pfds[npfd].fd     = rfd;
255         pfds[npfd].events = (rfd_want_read ? POLLIN : 0);
256         if (rfd >= 0 && pfds[npfd].events != 0)
257             ++npfd;
258 
259         pfds[npfd].fd     = wfd;
260         pfds[npfd].events = (wfd_want_write ? POLLOUT : 0);
261         if (wfd >= 0 && pfds[npfd].events != 0)
262             ++npfd;
263     }
264 
265     if (!ossl_assert(npfd != 0 || !ossl_time_is_infinite(deadline)))
266         /* Do not block forever; should not happen. */
267         return 0;
268 
269 # if defined(OPENSSL_THREADS)
270     if (mutex != NULL)
271         ossl_crypto_mutex_unlock(mutex);
272 # endif
273 
274     do {
275         if (ossl_time_is_infinite(deadline)) {
276             timeout_ms = -1;
277         } else {
278             now         = ossl_time_now();
279             timeout     = ossl_time_subtract(deadline, now);
280             timeout_ms  = ossl_time2ms(timeout);
281         }
282 
283         pres = poll(pfds, npfd, timeout_ms);
284     } while (pres == -1 && get_last_socket_error_is_eintr());
285 
286 # if defined(OPENSSL_THREADS)
287     if (mutex != NULL)
288         ossl_crypto_mutex_lock(mutex);
289 # endif
290 
291     return pres < 0 ? 0 : 1;
292 #endif
293 }
294 
poll_descriptor_to_fd(const BIO_POLL_DESCRIPTOR * d,int * fd)295 static int poll_descriptor_to_fd(const BIO_POLL_DESCRIPTOR *d, int *fd)
296 {
297     if (d == NULL || d->type == BIO_POLL_DESCRIPTOR_TYPE_NONE) {
298         *fd = INVALID_SOCKET;
299         return 1;
300     }
301 
302     if (d->type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD
303             || d->value.fd == INVALID_SOCKET)
304         return 0;
305 
306     *fd = d->value.fd;
307     return 1;
308 }
309 
310 /*
311  * Poll up to two abstract poll descriptors. Currently we only support
312  * poll descriptors which represent FDs.
313  *
314  * If mutex is non-NULL, it is assumed be a lock currently held for write and is
315  * unlocked for the duration of any wait.
316  *
317  * Precondition:   mutex is NULL or is held for write (unchecked)
318  * Postcondition:  mutex is NULL or is held for write (unless
319  *                   CRYPTO_THREAD_write_lock fails)
320  */
poll_two_descriptors(const BIO_POLL_DESCRIPTOR * r,int r_want_read,const BIO_POLL_DESCRIPTOR * w,int w_want_write,OSSL_TIME deadline,CRYPTO_MUTEX * mutex)321 static int poll_two_descriptors(const BIO_POLL_DESCRIPTOR *r, int r_want_read,
322                                 const BIO_POLL_DESCRIPTOR *w, int w_want_write,
323                                 OSSL_TIME deadline,
324                                 CRYPTO_MUTEX *mutex)
325 {
326     int rfd, wfd;
327 
328     if (!poll_descriptor_to_fd(r, &rfd)
329         || !poll_descriptor_to_fd(w, &wfd))
330         return 0;
331 
332     return poll_two_fds(rfd, r_want_read, wfd, w_want_write, deadline, mutex);
333 }
334 
335 /*
336  * Block until a predicate function evaluates to true.
337  *
338  * If mutex is non-NULL, it is assumed be a lock currently held for write and is
339  * unlocked for the duration of any wait.
340  *
341  * Precondition:   Must hold channel write lock (unchecked)
342  * Precondition:   mutex is NULL or is held for write (unchecked)
343  * Postcondition:  mutex is NULL or is held for write (unless
344  *                   CRYPTO_THREAD_write_lock fails)
345  */
ossl_quic_reactor_block_until_pred(QUIC_REACTOR * rtor,int (* pred)(void * arg),void * pred_arg,uint32_t flags,CRYPTO_MUTEX * mutex)346 int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor,
347                                        int (*pred)(void *arg), void *pred_arg,
348                                        uint32_t flags,
349                                        CRYPTO_MUTEX *mutex)
350 {
351     int res;
352 
353     for (;;) {
354         if ((flags & SKIP_FIRST_TICK) != 0)
355             flags &= ~SKIP_FIRST_TICK;
356         else
357             /* best effort */
358             ossl_quic_reactor_tick(rtor, 0);
359 
360         if ((res = pred(pred_arg)) != 0)
361             return res;
362 
363         if (!poll_two_descriptors(ossl_quic_reactor_get_poll_r(rtor),
364                                   ossl_quic_reactor_net_read_desired(rtor),
365                                   ossl_quic_reactor_get_poll_w(rtor),
366                                   ossl_quic_reactor_net_write_desired(rtor),
367                                   ossl_quic_reactor_get_tick_deadline(rtor),
368                                   mutex))
369             /*
370              * We don't actually care why the call succeeded (timeout, FD
371              * readiness), we just call reactor_tick and start trying to do I/O
372              * things again. If poll_two_fds returns 0, this is some other
373              * non-timeout failure and we should stop here.
374              *
375              * TODO(QUIC FUTURE): In the future we could avoid unnecessary
376              * syscalls by not retrying network I/O that isn't ready based
377              * on the result of the poll call. However this might be difficult
378              * because it requires we do the call to poll(2) or equivalent
379              * syscall ourselves, whereas in the general case the application
380              * does the polling and just calls SSL_handle_events().
381              * Implementing this optimisation in the future will probably
382              * therefore require API changes.
383              */
384             return 0;
385     }
386 }
387