xref: /openssl/crypto/bio/bss_dgram_pair.c (revision 572f290c)
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 
10 #include <stdio.h>
11 #include <errno.h>
12 #include "bio_local.h"
13 #include "internal/cryptlib.h"
14 #include "internal/safe_math.h"
15 
16 #if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK)
17 
18 OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
19 
20 /* ===========================================================================
21  * Byte-wise ring buffer which supports pushing and popping blocks of multiple
22  * bytes at a time.
23  */
24 struct ring_buf {
25     unsigned char *start; /* start of buffer */
26     size_t len; /* size of buffer allocation in bytes */
27     size_t count; /* number of bytes currently pushed */
28     /*
29      * These index into start. Where idx[0] == idx[1], the buffer is full
30      * (if count is nonzero) and empty otherwise.
31      */
32     size_t idx[2]; /* 0: head, 1: tail */
33 };
34 
ring_buf_init(struct ring_buf * r,size_t nbytes)35 static int ring_buf_init(struct ring_buf *r, size_t nbytes)
36 {
37     r->start = OPENSSL_malloc(nbytes);
38     if (r->start == NULL)
39         return 0;
40 
41     r->len = nbytes;
42     r->idx[0] = r->idx[1] = r->count = 0;
43     return 1;
44 }
45 
ring_buf_destroy(struct ring_buf * r)46 static void ring_buf_destroy(struct ring_buf *r)
47 {
48     OPENSSL_free(r->start);
49     r->start    = NULL;
50     r->len      = 0;
51     r->count    = 0;
52 }
53 
54 /*
55  * Get a pointer to the next place to write data to be pushed to the ring buffer
56  * (idx=0), or the next data to be popped from the ring buffer (idx=1). The
57  * pointer is written to *buf and the maximum number of bytes which can be
58  * read/written are written to *len. After writing data to the buffer, call
59  * ring_buf_push/pop() with the number of bytes actually read/written, which
60  * must not exceed the returned length.
61  */
ring_buf_head_tail(struct ring_buf * r,int idx,uint8_t ** buf,size_t * len)62 static void ring_buf_head_tail(struct ring_buf *r, int idx, uint8_t **buf, size_t *len)
63 {
64     size_t max_len = r->len - r->idx[idx];
65 
66     if (idx == 0 && max_len > r->len - r->count)
67         max_len = r->len - r->count;
68     if (idx == 1 && max_len > r->count)
69         max_len = r->count;
70 
71     *buf = (uint8_t *)r->start + r->idx[idx];
72     *len = max_len;
73 }
74 
75 #define ring_buf_head(r, buf, len) ring_buf_head_tail((r), 0, (buf), (len))
76 #define ring_buf_tail(r, buf, len) ring_buf_head_tail((r), 1, (buf), (len))
77 
78 /*
79  * Commit bytes to the ring buffer previously filled after a call to
80  * ring_buf_head().
81  */
ring_buf_push_pop(struct ring_buf * r,int idx,size_t num_bytes)82 static void ring_buf_push_pop(struct ring_buf *r, int idx, size_t num_bytes)
83 {
84     size_t new_idx;
85 
86     /* A single push/pop op cannot wrap around, though it can reach the end.
87      * If the caller adheres to the convention of using the length returned
88      * by ring_buf_head/tail(), this cannot happen.
89      */
90     if (!ossl_assert(num_bytes <= r->len - r->idx[idx]))
91         return;
92 
93     /*
94      * Must not overfill the buffer, or pop more than is in the buffer either.
95      */
96     if (!ossl_assert(idx != 0 ? num_bytes <= r->count
97                               : num_bytes + r->count <= r->len))
98         return;
99 
100     /* Update the index. */
101     new_idx = r->idx[idx] + num_bytes;
102     if (new_idx == r->len)
103         new_idx = 0;
104 
105     r->idx[idx] = new_idx;
106     if (idx != 0)
107         r->count -= num_bytes;
108     else
109         r->count += num_bytes;
110 }
111 
112 #define ring_buf_push(r, num_bytes) ring_buf_push_pop((r), 0, (num_bytes))
113 #define ring_buf_pop(r, num_bytes) ring_buf_push_pop((r), 1, (num_bytes))
114 
ring_buf_clear(struct ring_buf * r)115 static void ring_buf_clear(struct ring_buf *r)
116 {
117     r->idx[0] = r->idx[1] = r->count = 0;
118 }
119 
ring_buf_resize(struct ring_buf * r,size_t nbytes)120 static int ring_buf_resize(struct ring_buf *r, size_t nbytes)
121 {
122     unsigned char *new_start;
123 
124     if (r->start == NULL)
125         return ring_buf_init(r, nbytes);
126 
127     if (nbytes == r->len)
128         return 1;
129 
130     if (r->count > 0 && nbytes < r->len)
131         /* fail shrinking the ring buffer when there is any data in it */
132         return 0;
133 
134     new_start = OPENSSL_realloc(r->start, nbytes);
135     if (new_start == NULL)
136         return 0;
137 
138     /* Moving tail if it is after (or equal to) head */
139     if (r->count > 0) {
140         if (r->idx[0] <= r->idx[1]) {
141             size_t offset = nbytes - r->len;
142 
143             memmove(new_start + r->idx[1] + offset, new_start + r->idx[1],
144                     r->len - r->idx[1]);
145             r->idx[1] += offset;
146         }
147     } else {
148         /* just reset the head/tail because it might be pointing outside */
149         r->idx[0] = r->idx[1] = 0;
150     }
151 
152     r->start = new_start;
153     r->len = nbytes;
154 
155     return 1;
156 }
157 
158 /* ===========================================================================
159  * BIO_s_dgram_pair is documented in BIO_s_dgram_pair(3).
160  *
161  * INTERNAL DATA STRUCTURE
162  *
163  * This is managed internally by using a bytewise ring buffer which supports
164  * pushing and popping spans of multiple bytes at once. The ring buffer stores
165  * internal packets which look like this:
166  *
167  *   struct dgram_hdr hdr;
168  *   uint8_t data[];
169  *
170  * The header contains the length of the data and metadata such as
171  * source/destination addresses.
172  *
173  * The datagram pair BIO is designed to support both traditional
174  * BIO_read/BIO_write (likely to be used by applications) as well as
175  * BIO_recvmmsg/BIO_sendmmsg.
176  */
177 struct bio_dgram_pair_st;
178 static int dgram_pair_write(BIO *bio, const char *buf, int sz_);
179 static int dgram_pair_read(BIO *bio, char *buf, int sz_);
180 static int dgram_mem_read(BIO *bio, char *buf, int sz_);
181 static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr);
182 static long dgram_mem_ctrl(BIO *bio, int cmd, long num, void *ptr);
183 static int dgram_pair_init(BIO *bio);
184 static int dgram_mem_init(BIO *bio);
185 static int dgram_pair_free(BIO *bio);
186 static int dgram_pair_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
187                                size_t num_msg, uint64_t flags,
188                                size_t *num_processed);
189 static int dgram_pair_recvmmsg(BIO *b, BIO_MSG *msg, size_t stride,
190                                size_t num_msg, uint64_t flags,
191                                size_t *num_processed);
192 
193 static int dgram_pair_ctrl_destroy_bio_pair(BIO *bio1);
194 static size_t dgram_pair_read_inner(struct bio_dgram_pair_st *b, uint8_t *buf,
195                                     size_t sz);
196 
197 #define BIO_MSG_N(array, n) (*(BIO_MSG *)((char *)(array) + (n)*stride))
198 
199 static const BIO_METHOD dgram_pair_method = {
200     BIO_TYPE_DGRAM_PAIR,
201     "BIO dgram pair",
202     bwrite_conv,
203     dgram_pair_write,
204     bread_conv,
205     dgram_pair_read,
206     NULL, /* dgram_pair_puts */
207     NULL, /* dgram_pair_gets */
208     dgram_pair_ctrl,
209     dgram_pair_init,
210     dgram_pair_free,
211     NULL, /* dgram_pair_callback_ctrl */
212     dgram_pair_sendmmsg,
213     dgram_pair_recvmmsg,
214 };
215 
216 static const BIO_METHOD dgram_mem_method = {
217     BIO_TYPE_DGRAM_MEM,
218     "BIO dgram mem",
219     bwrite_conv,
220     dgram_pair_write,
221     bread_conv,
222     dgram_mem_read,
223     NULL, /* dgram_pair_puts */
224     NULL, /* dgram_pair_gets */
225     dgram_mem_ctrl,
226     dgram_mem_init,
227     dgram_pair_free,
228     NULL, /* dgram_pair_callback_ctrl */
229     dgram_pair_sendmmsg,
230     dgram_pair_recvmmsg,
231 };
232 
BIO_s_dgram_pair(void)233 const BIO_METHOD *BIO_s_dgram_pair(void)
234 {
235     return &dgram_pair_method;
236 }
237 
BIO_s_dgram_mem(void)238 const BIO_METHOD *BIO_s_dgram_mem(void)
239 {
240     return &dgram_mem_method;
241 }
242 
243 struct dgram_hdr {
244     size_t len; /* payload length in bytes, not including this struct */
245     BIO_ADDR src_addr, dst_addr; /* family == 0: not present */
246 };
247 
248 struct bio_dgram_pair_st {
249     /* The other half of the BIO pair. NULL for dgram_mem. */
250     BIO *peer;
251     /* Writes are directed to our own ringbuf and reads to our peer. */
252     struct ring_buf rbuf;
253     /* Requested size of rbuf buffer in bytes once we initialize. */
254     size_t req_buf_len;
255     /* Largest possible datagram size */
256     size_t mtu;
257     /* Capability flags. */
258     uint32_t cap;
259     /*
260      * This lock protects updates to our rbuf. Since writes are directed to our
261      * own rbuf, this means we use this lock for writes and our peer's lock for
262      * reads.
263      */
264     CRYPTO_RWLOCK *lock;
265     unsigned int no_trunc          : 1; /* Reads fail if they would truncate */
266     unsigned int local_addr_enable : 1; /* Can use BIO_MSG->local? */
267     unsigned int role              : 1; /* Determines lock order */
268     unsigned int grows_on_write    : 1; /* Set for BIO_s_dgram_mem only */
269 };
270 
271 #define MIN_BUF_LEN (1024)
272 
273 #define is_dgram_pair(b) (b->peer != NULL)
274 
dgram_pair_init(BIO * bio)275 static int dgram_pair_init(BIO *bio)
276 {
277     struct bio_dgram_pair_st *b = OPENSSL_zalloc(sizeof(*b));
278 
279     if (b == NULL)
280         return 0;
281 
282     b->mtu         = 1472;    /* conservative default MTU */
283     /* default buffer size */
284     b->req_buf_len = 9 * (sizeof(struct dgram_hdr) + b->mtu);
285 
286     b->lock = CRYPTO_THREAD_lock_new();
287     if (b->lock == NULL) {
288         OPENSSL_free(b);
289         return 0;
290     }
291 
292     bio->ptr = b;
293     return 1;
294 }
295 
dgram_mem_init(BIO * bio)296 static int dgram_mem_init(BIO *bio)
297 {
298     struct bio_dgram_pair_st *b;
299 
300     if (!dgram_pair_init(bio))
301         return 0;
302 
303     b = bio->ptr;
304 
305     if (ring_buf_init(&b->rbuf, b->req_buf_len) == 0) {
306         ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB);
307         return 0;
308     }
309 
310     b->grows_on_write = 1;
311 
312     bio->init = 1;
313     return 1;
314 }
315 
dgram_pair_free(BIO * bio)316 static int dgram_pair_free(BIO *bio)
317 {
318     struct bio_dgram_pair_st *b;
319 
320     if (bio == NULL)
321         return 0;
322 
323     b = bio->ptr;
324     if (!ossl_assert(b != NULL))
325         return 0;
326 
327     /* We are being freed. Disconnect any peer and destroy buffers. */
328     dgram_pair_ctrl_destroy_bio_pair(bio);
329 
330     CRYPTO_THREAD_lock_free(b->lock);
331     OPENSSL_free(b);
332     return 1;
333 }
334 
335 /* BIO_make_bio_pair (BIO_C_MAKE_BIO_PAIR) */
dgram_pair_ctrl_make_bio_pair(BIO * bio1,BIO * bio2)336 static int dgram_pair_ctrl_make_bio_pair(BIO *bio1, BIO *bio2)
337 {
338     struct bio_dgram_pair_st *b1, *b2;
339 
340     /* peer must be non-NULL. */
341     if (bio1 == NULL || bio2 == NULL) {
342         ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
343         return 0;
344     }
345 
346     /* Ensure the BIO we have been passed is actually a dgram pair BIO. */
347     if (bio1->method != &dgram_pair_method || bio2->method != &dgram_pair_method) {
348         ERR_raise_data(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT,
349                        "both BIOs must be BIO_dgram_pair");
350         return 0;
351     }
352 
353     b1 = bio1->ptr;
354     b2 = bio2->ptr;
355 
356     if (!ossl_assert(b1 != NULL && b2 != NULL)) {
357         ERR_raise(ERR_LIB_BIO, BIO_R_UNINITIALIZED);
358         return 0;
359     }
360 
361     /*
362      * This ctrl cannot be used to associate a BIO pair half which is already
363      * associated.
364      */
365     if (b1->peer != NULL || b2->peer != NULL) {
366         ERR_raise_data(ERR_LIB_BIO, BIO_R_IN_USE,
367                        "cannot associate a BIO_dgram_pair which is already in use");
368         return 0;
369     }
370 
371     if (!ossl_assert(b1->req_buf_len >= MIN_BUF_LEN
372                         && b2->req_buf_len >= MIN_BUF_LEN)) {
373         ERR_raise(ERR_LIB_BIO, BIO_R_UNINITIALIZED);
374         return 0;
375     }
376 
377     if (b1->rbuf.len != b1->req_buf_len)
378         if (ring_buf_init(&b1->rbuf, b1->req_buf_len) == 0) {
379             ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB);
380             return 0;
381         }
382 
383     if (b2->rbuf.len != b2->req_buf_len)
384         if (ring_buf_init(&b2->rbuf, b2->req_buf_len) == 0) {
385             ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB);
386             ring_buf_destroy(&b1->rbuf);
387             return 0;
388         }
389 
390     b1->peer    = bio2;
391     b2->peer    = bio1;
392     b1->role    = 0;
393     b2->role    = 1;
394     bio1->init  = 1;
395     bio2->init  = 1;
396     return 1;
397 }
398 
399 /* BIO_destroy_bio_pair (BIO_C_DESTROY_BIO_PAIR) */
dgram_pair_ctrl_destroy_bio_pair(BIO * bio1)400 static int dgram_pair_ctrl_destroy_bio_pair(BIO *bio1)
401 {
402     BIO *bio2;
403     struct bio_dgram_pair_st *b1 = bio1->ptr, *b2;
404 
405     ring_buf_destroy(&b1->rbuf);
406     bio1->init = 0;
407 
408     /* Early return if we don't have a peer. */
409     if (b1->peer == NULL)
410         return 1;
411 
412     bio2 = b1->peer;
413     b2 = bio2->ptr;
414 
415     /* Invariant. */
416     if (!ossl_assert(b2->peer == bio1))
417         return 0;
418 
419     /* Free buffers. */
420     ring_buf_destroy(&b2->rbuf);
421 
422     bio2->init = 0;
423     b1->peer = NULL;
424     b2->peer = NULL;
425     return 1;
426 }
427 
428 /* BIO_eof (BIO_CTRL_EOF) */
dgram_pair_ctrl_eof(BIO * bio)429 static int dgram_pair_ctrl_eof(BIO *bio)
430 {
431     struct bio_dgram_pair_st *b = bio->ptr, *peerb;
432 
433     if (!ossl_assert(b != NULL))
434         return -1;
435 
436     /* If we aren't initialized, we can never read anything */
437     if (!bio->init)
438         return 1;
439     if (!is_dgram_pair(b))
440         return 0;
441 
442 
443     peerb = b->peer->ptr;
444     if (!ossl_assert(peerb != NULL))
445         return -1;
446 
447     /*
448      * Since we are emulating datagram semantics, never indicate EOF so long as
449      * we have a peer.
450      */
451     return 0;
452 }
453 
454 /* BIO_set_write_buf_size (BIO_C_SET_WRITE_BUF_SIZE) */
dgram_pair_ctrl_set_write_buf_size(BIO * bio,size_t len)455 static int dgram_pair_ctrl_set_write_buf_size(BIO *bio, size_t len)
456 {
457     struct bio_dgram_pair_st *b = bio->ptr;
458 
459     /* Changing buffer sizes is not permitted while a peer is connected. */
460     if (b->peer != NULL) {
461         ERR_raise(ERR_LIB_BIO, BIO_R_IN_USE);
462         return 0;
463     }
464 
465     /* Enforce minimum size. */
466     if (len < MIN_BUF_LEN)
467         len = MIN_BUF_LEN;
468 
469     if (b->rbuf.start != NULL) {
470         if (!ring_buf_resize(&b->rbuf, len))
471             return 0;
472     }
473 
474     b->req_buf_len = len;
475     b->grows_on_write = 0;
476     return 1;
477 }
478 
479 /* BIO_reset (BIO_CTRL_RESET) */
dgram_pair_ctrl_reset(BIO * bio)480 static int dgram_pair_ctrl_reset(BIO *bio)
481 {
482     struct bio_dgram_pair_st *b = bio->ptr;
483 
484     ring_buf_clear(&b->rbuf);
485     return 1;
486 }
487 
488 /* BIO_pending (BIO_CTRL_PENDING) (Threadsafe) */
dgram_pair_ctrl_pending(BIO * bio)489 static size_t dgram_pair_ctrl_pending(BIO *bio)
490 {
491     size_t saved_idx, saved_count;
492     struct bio_dgram_pair_st *b = bio->ptr, *readb;
493     struct dgram_hdr hdr;
494     size_t l;
495 
496     /* Safe to check; init may not change during this call */
497     if (!bio->init)
498         return 0;
499     if (is_dgram_pair(b))
500         readb = b->peer->ptr;
501     else
502         readb = b;
503 
504     if (CRYPTO_THREAD_write_lock(readb->lock) == 0)
505         return 0;
506 
507     saved_idx   = readb->rbuf.idx[1];
508     saved_count = readb->rbuf.count;
509 
510     l = dgram_pair_read_inner(readb, (uint8_t *)&hdr, sizeof(hdr));
511 
512     readb->rbuf.idx[1] = saved_idx;
513     readb->rbuf.count  = saved_count;
514 
515     CRYPTO_THREAD_unlock(readb->lock);
516 
517     if (!ossl_assert(l == 0 || l == sizeof(hdr)))
518         return 0;
519 
520     return l > 0 ? hdr.len : 0;
521 }
522 
523 /* BIO_get_write_guarantee (BIO_C_GET_WRITE_GUARANTEE) (Threadsafe) */
dgram_pair_ctrl_get_write_guarantee(BIO * bio)524 static size_t dgram_pair_ctrl_get_write_guarantee(BIO *bio)
525 {
526     size_t l;
527     struct bio_dgram_pair_st *b = bio->ptr;
528 
529     if (CRYPTO_THREAD_read_lock(b->lock) == 0)
530         return 0;
531 
532     l = b->rbuf.len - b->rbuf.count;
533     if (l >= sizeof(struct dgram_hdr))
534         l -= sizeof(struct dgram_hdr);
535 
536     /*
537      * If the amount of buffer space would not be enough to accommodate the
538      * worst-case size of a datagram, report no space available.
539      */
540     if (l < b->mtu)
541         l = 0;
542 
543     CRYPTO_THREAD_unlock(b->lock);
544     return l;
545 }
546 
547 /* BIO_dgram_get_local_addr_cap (BIO_CTRL_DGRAM_GET_LOCAL_ADDR_CAP) */
dgram_pair_ctrl_get_local_addr_cap(BIO * bio)548 static int dgram_pair_ctrl_get_local_addr_cap(BIO *bio)
549 {
550     struct bio_dgram_pair_st *b = bio->ptr, *readb;
551 
552     if (!bio->init)
553         return 0;
554 
555     if (is_dgram_pair(b))
556         readb = b->peer->ptr;
557     else
558         readb = b;
559 
560     return (~readb->cap & (BIO_DGRAM_CAP_HANDLES_SRC_ADDR
561                            | BIO_DGRAM_CAP_PROVIDES_DST_ADDR)) == 0;
562 }
563 
564 /* BIO_dgram_get_effective_caps (BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS) */
dgram_pair_ctrl_get_effective_caps(BIO * bio)565 static int dgram_pair_ctrl_get_effective_caps(BIO *bio)
566 {
567     struct bio_dgram_pair_st *b = bio->ptr, *peerb;
568 
569     if (b->peer == NULL)
570         return 0;
571 
572     peerb = b->peer->ptr;
573 
574     return peerb->cap;
575 }
576 
577 /* BIO_dgram_get_caps (BIO_CTRL_DGRAM_GET_CAPS) */
dgram_pair_ctrl_get_caps(BIO * bio)578 static uint32_t dgram_pair_ctrl_get_caps(BIO *bio)
579 {
580     struct bio_dgram_pair_st *b = bio->ptr;
581 
582     return b->cap;
583 }
584 
585 /* BIO_dgram_set_caps (BIO_CTRL_DGRAM_SET_CAPS) */
dgram_pair_ctrl_set_caps(BIO * bio,uint32_t caps)586 static int dgram_pair_ctrl_set_caps(BIO *bio, uint32_t caps)
587 {
588     struct bio_dgram_pair_st *b = bio->ptr;
589 
590     b->cap = caps;
591     return 1;
592 }
593 
594 /* BIO_dgram_get_local_addr_enable (BIO_CTRL_DGRAM_GET_LOCAL_ADDR_ENABLE) */
dgram_pair_ctrl_get_local_addr_enable(BIO * bio)595 static int dgram_pair_ctrl_get_local_addr_enable(BIO *bio)
596 {
597     struct bio_dgram_pair_st *b = bio->ptr;
598 
599     return b->local_addr_enable;
600 }
601 
602 /* BIO_dgram_set_local_addr_enable (BIO_CTRL_DGRAM_SET_LOCAL_ADDR_ENABLE) */
dgram_pair_ctrl_set_local_addr_enable(BIO * bio,int enable)603 static int dgram_pair_ctrl_set_local_addr_enable(BIO *bio, int enable)
604 {
605     struct bio_dgram_pair_st *b = bio->ptr;
606 
607     if (dgram_pair_ctrl_get_local_addr_cap(bio) == 0)
608         return 0;
609 
610     b->local_addr_enable = (enable != 0 ? 1 : 0);
611     return 1;
612 }
613 
614 /* BIO_dgram_get_mtu (BIO_CTRL_DGRAM_GET_MTU) */
dgram_pair_ctrl_get_mtu(BIO * bio)615 static int dgram_pair_ctrl_get_mtu(BIO *bio)
616 {
617     struct bio_dgram_pair_st *b = bio->ptr;
618 
619     return b->mtu;
620 }
621 
622 /* BIO_dgram_set_mtu (BIO_CTRL_DGRAM_SET_MTU) */
dgram_pair_ctrl_set_mtu(BIO * bio,size_t mtu)623 static int dgram_pair_ctrl_set_mtu(BIO *bio, size_t mtu)
624 {
625     struct bio_dgram_pair_st *b = bio->ptr, *peerb;
626 
627     b->mtu = mtu;
628 
629     if (b->peer != NULL) {
630         peerb = b->peer->ptr;
631         peerb->mtu = mtu;
632     }
633 
634     return 1;
635 }
636 
637 /* Partially threadsafe (some commands) */
dgram_mem_ctrl(BIO * bio,int cmd,long num,void * ptr)638 static long dgram_mem_ctrl(BIO *bio, int cmd, long num, void *ptr)
639 {
640     long ret = 1;
641     struct bio_dgram_pair_st *b = bio->ptr;
642 
643     if (!ossl_assert(b != NULL))
644         return 0;
645 
646     switch (cmd) {
647     /*
648      * BIO_set_write_buf_size: Set the size of the ring buffer used for storing
649      * datagrams. No more writes can be performed once the buffer is filled up,
650      * until reads are performed. This cannot be used after a peer is connected.
651      */
652     case BIO_C_SET_WRITE_BUF_SIZE: /* Non-threadsafe */
653         ret = (long)dgram_pair_ctrl_set_write_buf_size(bio, (size_t)num);
654         break;
655 
656     /*
657      * BIO_get_write_buf_size: Get ring buffer size.
658      */
659     case BIO_C_GET_WRITE_BUF_SIZE: /* Non-threadsafe */
660         ret = (long)b->req_buf_len;
661         break;
662 
663     /*
664      * BIO_reset: Clear all data which was written to this side of the pair.
665      */
666     case BIO_CTRL_RESET: /* Non-threadsafe */
667         dgram_pair_ctrl_reset(bio);
668         break;
669 
670     /*
671      * BIO_get_write_guarantee: Any BIO_write providing a buffer less than or
672      * equal to this value is guaranteed to succeed.
673      */
674     case BIO_C_GET_WRITE_GUARANTEE: /* Threadsafe */
675         ret = (long)dgram_pair_ctrl_get_write_guarantee(bio);
676         break;
677 
678     /* BIO_pending: Bytes available to read. */
679     case BIO_CTRL_PENDING: /* Threadsafe */
680         ret = (long)dgram_pair_ctrl_pending(bio);
681         break;
682 
683     /* BIO_flush: No-op. */
684     case BIO_CTRL_FLUSH: /* Threadsafe */
685         break;
686 
687     /* BIO_dgram_get_no_trunc */
688     case BIO_CTRL_DGRAM_GET_NO_TRUNC: /* Non-threadsafe */
689         ret = (long)b->no_trunc;
690         break;
691 
692     /* BIO_dgram_set_no_trunc */
693     case BIO_CTRL_DGRAM_SET_NO_TRUNC: /* Non-threadsafe */
694         b->no_trunc = (num > 0);
695         break;
696 
697     /* BIO_dgram_get_local_addr_enable */
698     case BIO_CTRL_DGRAM_GET_LOCAL_ADDR_ENABLE: /* Non-threadsafe */
699         *(int *)ptr = (int)dgram_pair_ctrl_get_local_addr_enable(bio);
700         break;
701 
702     /* BIO_dgram_set_local_addr_enable */
703     case BIO_CTRL_DGRAM_SET_LOCAL_ADDR_ENABLE: /* Non-threadsafe */
704         ret = (long)dgram_pair_ctrl_set_local_addr_enable(bio, num);
705         break;
706 
707     /* BIO_dgram_get_local_addr_cap: Can local addresses be supported? */
708     case BIO_CTRL_DGRAM_GET_LOCAL_ADDR_CAP: /* Non-threadsafe */
709         ret = (long)dgram_pair_ctrl_get_local_addr_cap(bio);
710         break;
711 
712     /* BIO_dgram_get_effective_caps */
713     case BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS: /* Non-threadsafe */
714     /* BIO_dgram_get_caps */
715     case BIO_CTRL_DGRAM_GET_CAPS: /* Non-threadsafe */
716         ret = (long)dgram_pair_ctrl_get_caps(bio);
717         break;
718 
719     /* BIO_dgram_set_caps */
720     case BIO_CTRL_DGRAM_SET_CAPS: /* Non-threadsafe */
721         ret = (long)dgram_pair_ctrl_set_caps(bio, (uint32_t)num);
722         break;
723 
724     /* BIO_dgram_get_mtu */
725     case BIO_CTRL_DGRAM_GET_MTU: /* Non-threadsafe */
726         ret = (long)dgram_pair_ctrl_get_mtu(bio);
727         break;
728 
729     /* BIO_dgram_set_mtu */
730     case BIO_CTRL_DGRAM_SET_MTU: /* Non-threadsafe */
731         ret = (long)dgram_pair_ctrl_set_mtu(bio, (uint32_t)num);
732         break;
733 
734     /*
735      * BIO_eof: Returns whether this half of the BIO pair is empty of data to
736      * read.
737      */
738     case BIO_CTRL_EOF: /* Non-threadsafe */
739         ret = (long)dgram_pair_ctrl_eof(bio);
740         break;
741 
742     default:
743         ret = 0;
744         break;
745     }
746 
747     return ret;
748 }
749 
dgram_pair_ctrl(BIO * bio,int cmd,long num,void * ptr)750 static long dgram_pair_ctrl(BIO *bio, int cmd, long num, void *ptr)
751 {
752     long ret = 1;
753 
754     switch (cmd) {
755     /*
756      * BIO_make_bio_pair: this is usually used by BIO_new_dgram_pair, though it
757      * may be used manually after manually creating each half of a BIO pair
758      * using BIO_new. This only needs to be called on one of the BIOs.
759      */
760     case BIO_C_MAKE_BIO_PAIR: /* Non-threadsafe */
761         ret = (long)dgram_pair_ctrl_make_bio_pair(bio, (BIO *)ptr);
762         break;
763 
764     /*
765      * BIO_destroy_bio_pair: Manually disconnect two halves of a BIO pair so
766      * that they are no longer peers.
767      */
768     case BIO_C_DESTROY_BIO_PAIR: /* Non-threadsafe */
769         dgram_pair_ctrl_destroy_bio_pair(bio);
770         break;
771 
772     /* BIO_dgram_get_effective_caps */
773     case BIO_CTRL_DGRAM_GET_EFFECTIVE_CAPS: /* Non-threadsafe */
774         ret = (long)dgram_pair_ctrl_get_effective_caps(bio);
775         break;
776 
777     default:
778         ret = dgram_mem_ctrl(bio, cmd, num, ptr);
779         break;
780     }
781 
782     return ret;
783 }
784 
BIO_new_bio_dgram_pair(BIO ** pbio1,size_t writebuf1,BIO ** pbio2,size_t writebuf2)785 int BIO_new_bio_dgram_pair(BIO **pbio1, size_t writebuf1,
786                            BIO **pbio2, size_t writebuf2)
787 {
788     int ret = 0;
789     long r;
790     BIO *bio1 = NULL, *bio2 = NULL;
791 
792     bio1 = BIO_new(BIO_s_dgram_pair());
793     if (bio1 == NULL)
794         goto err;
795 
796     bio2 = BIO_new(BIO_s_dgram_pair());
797     if (bio2 == NULL)
798         goto err;
799 
800     if (writebuf1 > 0) {
801         r = BIO_set_write_buf_size(bio1, writebuf1);
802         if (r == 0)
803             goto err;
804     }
805 
806     if (writebuf2 > 0) {
807         r = BIO_set_write_buf_size(bio2, writebuf2);
808         if (r == 0)
809             goto err;
810     }
811 
812     r = BIO_make_bio_pair(bio1, bio2);
813     if (r == 0)
814         goto err;
815 
816     ret = 1;
817 err:
818     if (ret == 0) {
819         BIO_free(bio1);
820         bio1 = NULL;
821         BIO_free(bio2);
822         bio2 = NULL;
823     }
824 
825     *pbio1 = bio1;
826     *pbio2 = bio2;
827     return ret;
828 }
829 
830 /* Must hold peer write lock */
dgram_pair_read_inner(struct bio_dgram_pair_st * b,uint8_t * buf,size_t sz)831 static size_t dgram_pair_read_inner(struct bio_dgram_pair_st *b, uint8_t *buf, size_t sz)
832 {
833     size_t total_read = 0;
834 
835     /*
836      * We repeat pops from the ring buffer for as long as we have more
837      * application *buffer to fill until we fail. We may not be able to pop
838      * enough data to fill the buffer in one operation if the ring buffer wraps
839      * around, but there may still be more data available.
840      */
841     while (sz > 0) {
842         uint8_t *src_buf = NULL;
843         size_t src_len = 0;
844 
845         /*
846          * There are two BIO instances, each with a ringbuf. We read from the
847          * peer ringbuf and write to our own ringbuf.
848          */
849         ring_buf_tail(&b->rbuf, &src_buf, &src_len);
850         if (src_len == 0)
851             break;
852 
853         if (src_len > sz)
854             src_len = sz;
855 
856         if (buf != NULL)
857             memcpy(buf, src_buf, src_len);
858 
859         ring_buf_pop(&b->rbuf, src_len);
860 
861         if (buf != NULL)
862             buf += src_len;
863         total_read  += src_len;
864         sz          -= src_len;
865     }
866 
867     return total_read;
868 }
869 
870 /*
871  * Must hold peer write lock. Returns number of bytes processed or negated BIO
872  * response code.
873  */
dgram_pair_read_actual(BIO * bio,char * buf,size_t sz,BIO_ADDR * local,BIO_ADDR * peer,int is_multi)874 static ossl_ssize_t dgram_pair_read_actual(BIO *bio, char *buf, size_t sz,
875                                            BIO_ADDR *local, BIO_ADDR *peer,
876                                            int is_multi)
877 {
878     size_t l, trunc = 0, saved_idx, saved_count;
879     struct bio_dgram_pair_st *b = bio->ptr, *readb;
880     struct dgram_hdr hdr;
881 
882     if (!is_multi)
883         BIO_clear_retry_flags(bio);
884 
885     if (!bio->init)
886         return -BIO_R_UNINITIALIZED;
887 
888     if (!ossl_assert(b != NULL))
889         return -BIO_R_TRANSFER_ERROR;
890 
891     if (is_dgram_pair(b))
892         readb = b->peer->ptr;
893     else
894         readb = b;
895     if (!ossl_assert(readb != NULL && readb->rbuf.start != NULL))
896         return -BIO_R_TRANSFER_ERROR;
897 
898     if (sz > 0 && buf == NULL)
899         return -BIO_R_INVALID_ARGUMENT;
900 
901     /* If the caller wants to know the local address, it must be enabled */
902     if (local != NULL && b->local_addr_enable == 0)
903         return -BIO_R_LOCAL_ADDR_NOT_AVAILABLE;
904 
905     /* Read the header. */
906     saved_idx   = readb->rbuf.idx[1];
907     saved_count = readb->rbuf.count;
908     l = dgram_pair_read_inner(readb, (uint8_t *)&hdr, sizeof(hdr));
909     if (l == 0) {
910         /* Buffer was empty. */
911         if (!is_multi)
912             BIO_set_retry_read(bio);
913         return -BIO_R_NON_FATAL;
914     }
915 
916     if (!ossl_assert(l == sizeof(hdr)))
917         /*
918          * This should not be possible as headers (and their following payloads)
919          * should always be written atomically.
920          */
921         return -BIO_R_BROKEN_PIPE;
922 
923     if (sz > hdr.len) {
924         sz = hdr.len;
925     } else if (sz < hdr.len) {
926         /* Truncation is occurring. */
927         trunc = hdr.len - sz;
928         if (b->no_trunc) {
929             /* Restore original state. */
930             readb->rbuf.idx[1] = saved_idx;
931             readb->rbuf.count  = saved_count;
932             return -BIO_R_NON_FATAL;
933         }
934     }
935 
936     l = dgram_pair_read_inner(readb, (uint8_t *)buf, sz);
937     if (!ossl_assert(l == sz))
938         /* We were somehow not able to read the entire datagram. */
939         return -BIO_R_TRANSFER_ERROR;
940 
941     /*
942      * If the datagram was truncated due to an inadequate buffer, discard the
943      * remainder.
944      */
945     if (trunc > 0 && !ossl_assert(dgram_pair_read_inner(readb, NULL, trunc) == trunc))
946         /* We were somehow not able to read/skip the entire datagram. */
947         return -BIO_R_TRANSFER_ERROR;
948 
949     if (local != NULL)
950         *local = hdr.dst_addr;
951     if (peer != NULL)
952         *peer  = hdr.src_addr;
953 
954     return (ossl_ssize_t)l;
955 }
956 
957 /* Threadsafe */
dgram_pair_lock_both_write(struct bio_dgram_pair_st * a,struct bio_dgram_pair_st * b)958 static int dgram_pair_lock_both_write(struct bio_dgram_pair_st *a,
959                                       struct bio_dgram_pair_st *b)
960 {
961     struct bio_dgram_pair_st *x, *y;
962 
963     x = (a->role == 1) ? a : b;
964     y = (a->role == 1) ? b : a;
965 
966     if (!ossl_assert(a->role != b->role))
967         return 0;
968 
969     if (!ossl_assert(a != b && x != y))
970         return 0;
971 
972     if (CRYPTO_THREAD_write_lock(x->lock) == 0)
973         return 0;
974 
975     if (CRYPTO_THREAD_write_lock(y->lock) == 0) {
976         CRYPTO_THREAD_unlock(x->lock);
977         return 0;
978     }
979 
980     return 1;
981 }
982 
dgram_pair_unlock_both(struct bio_dgram_pair_st * a,struct bio_dgram_pair_st * b)983 static void dgram_pair_unlock_both(struct bio_dgram_pair_st *a,
984                                    struct bio_dgram_pair_st *b)
985 {
986     CRYPTO_THREAD_unlock(a->lock);
987     CRYPTO_THREAD_unlock(b->lock);
988 }
989 
990 /* Threadsafe */
dgram_pair_read(BIO * bio,char * buf,int sz_)991 static int dgram_pair_read(BIO *bio, char *buf, int sz_)
992 {
993     int ret;
994     ossl_ssize_t l;
995     struct bio_dgram_pair_st *b = bio->ptr, *peerb;
996 
997     if (sz_ < 0) {
998         ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
999         return -1;
1000     }
1001 
1002     if (b->peer == NULL) {
1003         ERR_raise(ERR_LIB_BIO, BIO_R_BROKEN_PIPE);
1004         return -1;
1005     }
1006 
1007     peerb = b->peer->ptr;
1008 
1009     /*
1010      * For BIO_read we have to acquire both locks because we touch the retry
1011      * flags on the local bio. (This is avoided in the recvmmsg case as it does
1012      * not touch the retry flags.)
1013      */
1014     if (dgram_pair_lock_both_write(peerb, b) == 0) {
1015         ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
1016         return -1;
1017     }
1018 
1019     l = dgram_pair_read_actual(bio, buf, (size_t)sz_, NULL, NULL, 0);
1020     if (l < 0) {
1021         if (l != -BIO_R_NON_FATAL)
1022             ERR_raise(ERR_LIB_BIO, -l);
1023         ret = -1;
1024     } else {
1025         ret = (int)l;
1026     }
1027 
1028     dgram_pair_unlock_both(peerb, b);
1029     return ret;
1030 }
1031 
1032 /* Threadsafe */
dgram_pair_recvmmsg(BIO * bio,BIO_MSG * msg,size_t stride,size_t num_msg,uint64_t flags,size_t * num_processed)1033 static int dgram_pair_recvmmsg(BIO *bio, BIO_MSG *msg,
1034                                size_t stride, size_t num_msg,
1035                                uint64_t flags,
1036                                size_t *num_processed)
1037 {
1038     int ret;
1039     ossl_ssize_t l;
1040     BIO_MSG *m;
1041     size_t i;
1042     struct bio_dgram_pair_st *b = bio->ptr, *readb;
1043 
1044     if (num_msg == 0) {
1045         *num_processed = 0;
1046         return 1;
1047     }
1048 
1049     if (!bio->init) {
1050         ERR_raise(ERR_LIB_BIO, BIO_R_BROKEN_PIPE);
1051         *num_processed = 0;
1052         return 0;
1053     }
1054 
1055     if (is_dgram_pair(b))
1056         readb = b->peer->ptr;
1057     else
1058         readb = b;
1059 
1060     if (CRYPTO_THREAD_write_lock(readb->lock) == 0) {
1061         ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
1062         *num_processed = 0;
1063         return 0;
1064     }
1065 
1066     for (i = 0; i < num_msg; ++i) {
1067         m = &BIO_MSG_N(msg, i);
1068         l = dgram_pair_read_actual(bio, m->data, m->data_len,
1069                                    m->local, m->peer, 1);
1070         if (l < 0) {
1071             *num_processed = i;
1072             if (i > 0) {
1073                 ret = 1;
1074             } else {
1075                 ERR_raise(ERR_LIB_BIO, -l);
1076                 ret = 0;
1077             }
1078             goto out;
1079         }
1080 
1081         m->data_len = l;
1082         m->flags    = 0;
1083     }
1084 
1085     *num_processed = i;
1086     ret = 1;
1087 out:
1088     CRYPTO_THREAD_unlock(readb->lock);
1089     return ret;
1090 }
1091 
1092 /* Threadsafe */
dgram_mem_read(BIO * bio,char * buf,int sz_)1093 static int dgram_mem_read(BIO *bio, char *buf, int sz_)
1094 {
1095     int ret;
1096     ossl_ssize_t l;
1097     struct bio_dgram_pair_st *b = bio->ptr;
1098 
1099     if (sz_ < 0) {
1100         ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
1101         return -1;
1102     }
1103 
1104     if (CRYPTO_THREAD_write_lock(b->lock) == 0) {
1105         ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
1106         return -1;
1107     }
1108 
1109     l = dgram_pair_read_actual(bio, buf, (size_t)sz_, NULL, NULL, 0);
1110     if (l < 0) {
1111         if (l != -BIO_R_NON_FATAL)
1112             ERR_raise(ERR_LIB_BIO, -l);
1113         ret = -1;
1114     } else {
1115         ret = (int)l;
1116     }
1117 
1118     CRYPTO_THREAD_unlock(b->lock);
1119     return ret;
1120 }
1121 
1122 /*
1123  * Calculate the array growth based on the target size.
1124  *
1125  * The growth factor is a rational number and is defined by a numerator
1126  * and a denominator.  According to Andrew Koenig in his paper "Why Are
1127  * Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less
1128  * than the golden ratio (1.618...).
1129  *
1130  * We use an expansion factor of 8 / 5 = 1.6
1131  */
1132 static const size_t max_rbuf_size = SIZE_MAX / 2; /* unlimited in practice */
compute_rbuf_growth(size_t target,size_t current)1133 static ossl_inline size_t compute_rbuf_growth(size_t target, size_t current)
1134 {
1135     int err = 0;
1136 
1137     while (current < target) {
1138         if (current >= max_rbuf_size)
1139             return 0;
1140 
1141         current = safe_muldiv_size_t(current, 8, 5, &err);
1142         if (err)
1143             return 0;
1144         if (current >= max_rbuf_size)
1145             current = max_rbuf_size;
1146     }
1147     return current;
1148 }
1149 
1150 /* Must hold local write lock */
dgram_pair_write_inner(struct bio_dgram_pair_st * b,const uint8_t * buf,size_t sz)1151 static size_t dgram_pair_write_inner(struct bio_dgram_pair_st *b,
1152                                      const uint8_t *buf, size_t sz)
1153 {
1154     size_t total_written = 0;
1155 
1156     /*
1157      * We repeat pushes to the ring buffer for as long as we have data until we
1158      * fail. We may not be able to push in one operation if the ring buffer
1159      * wraps around, but there may still be more room for data.
1160      */
1161     while (sz > 0) {
1162         size_t dst_len;
1163         uint8_t *dst_buf;
1164 
1165         /*
1166          * There are two BIO instances, each with a ringbuf. We write to our own
1167          * ringbuf and read from the peer ringbuf.
1168          */
1169         ring_buf_head(&b->rbuf, &dst_buf, &dst_len);
1170         if (dst_len == 0) {
1171             size_t new_len;
1172 
1173             if (!b->grows_on_write) /* resize only if size not set explicitly */
1174                 break;
1175             /* increase the size */
1176             new_len = compute_rbuf_growth(b->req_buf_len + sz, b->req_buf_len);
1177             if (new_len == 0 || !ring_buf_resize(&b->rbuf, new_len))
1178                 break;
1179             b->req_buf_len = new_len;
1180         }
1181 
1182         if (dst_len > sz)
1183             dst_len = sz;
1184 
1185         memcpy(dst_buf, buf, dst_len);
1186         ring_buf_push(&b->rbuf, dst_len);
1187 
1188         buf             += dst_len;
1189         sz              -= dst_len;
1190         total_written   += dst_len;
1191     }
1192 
1193     return total_written;
1194 }
1195 
1196 /*
1197  * Must hold local write lock. Returns number of bytes processed or negated BIO
1198  * response code.
1199  */
dgram_pair_write_actual(BIO * bio,const char * buf,size_t sz,const BIO_ADDR * local,const BIO_ADDR * peer,int is_multi)1200 static ossl_ssize_t dgram_pair_write_actual(BIO *bio, const char *buf, size_t sz,
1201                                             const BIO_ADDR *local, const BIO_ADDR *peer,
1202                                             int is_multi)
1203 {
1204     static const BIO_ADDR zero_addr;
1205     size_t saved_idx, saved_count;
1206     struct bio_dgram_pair_st *b = bio->ptr, *readb;
1207     struct dgram_hdr hdr = {0};
1208 
1209     if (!is_multi)
1210         BIO_clear_retry_flags(bio);
1211 
1212     if (!bio->init)
1213         return -BIO_R_UNINITIALIZED;
1214 
1215     if (!ossl_assert(b != NULL && b->rbuf.start != NULL))
1216         return -BIO_R_TRANSFER_ERROR;
1217 
1218     if (sz > 0 && buf == NULL)
1219         return -BIO_R_INVALID_ARGUMENT;
1220 
1221     if (local != NULL && b->local_addr_enable == 0)
1222         return -BIO_R_LOCAL_ADDR_NOT_AVAILABLE;
1223 
1224     if (is_dgram_pair(b))
1225         readb = b->peer->ptr;
1226     else
1227         readb = b;
1228     if (peer != NULL && (readb->cap & BIO_DGRAM_CAP_HANDLES_DST_ADDR) == 0)
1229         return -BIO_R_PEER_ADDR_NOT_AVAILABLE;
1230 
1231     hdr.len = sz;
1232     hdr.dst_addr = (peer != NULL ? *peer : zero_addr);
1233     hdr.src_addr = (local != NULL ? *local : zero_addr);
1234 
1235     saved_idx   = b->rbuf.idx[0];
1236     saved_count = b->rbuf.count;
1237     if (dgram_pair_write_inner(b, (const uint8_t *)&hdr, sizeof(hdr)) != sizeof(hdr)
1238             || dgram_pair_write_inner(b, (const uint8_t *)buf, sz) != sz) {
1239         /*
1240          * We were not able to push the header and the entirety of the payload
1241          * onto the ring buffer, so abort and roll back the ring buffer state.
1242          */
1243         b->rbuf.idx[0] = saved_idx;
1244         b->rbuf.count  = saved_count;
1245         if (!is_multi)
1246             BIO_set_retry_write(bio);
1247         return -BIO_R_NON_FATAL;
1248     }
1249 
1250     return sz;
1251 }
1252 
1253 /* Threadsafe */
dgram_pair_write(BIO * bio,const char * buf,int sz_)1254 static int dgram_pair_write(BIO *bio, const char *buf, int sz_)
1255 {
1256     int ret;
1257     ossl_ssize_t l;
1258     struct bio_dgram_pair_st *b = bio->ptr;
1259 
1260     if (sz_ < 0) {
1261         ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
1262         return -1;
1263     }
1264 
1265     if (CRYPTO_THREAD_write_lock(b->lock) == 0) {
1266         ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
1267         return -1;
1268     }
1269 
1270     l = dgram_pair_write_actual(bio, buf, (size_t)sz_, NULL, NULL, 0);
1271     if (l < 0) {
1272         ERR_raise(ERR_LIB_BIO, -l);
1273         ret = -1;
1274     } else {
1275         ret = (int)l;
1276     }
1277 
1278     CRYPTO_THREAD_unlock(b->lock);
1279     return ret;
1280 }
1281 
1282 /* Threadsafe */
dgram_pair_sendmmsg(BIO * bio,BIO_MSG * msg,size_t stride,size_t num_msg,uint64_t flags,size_t * num_processed)1283 static int dgram_pair_sendmmsg(BIO *bio, BIO_MSG *msg,
1284                                size_t stride, size_t num_msg,
1285                                uint64_t flags, size_t *num_processed)
1286 {
1287     ossl_ssize_t ret, l;
1288     BIO_MSG *m;
1289     size_t i;
1290     struct bio_dgram_pair_st *b = bio->ptr;
1291 
1292     if (num_msg == 0) {
1293         *num_processed = 0;
1294         return 1;
1295     }
1296 
1297     if (CRYPTO_THREAD_write_lock(b->lock) == 0) {
1298         ERR_raise(ERR_LIB_BIO, ERR_R_UNABLE_TO_GET_WRITE_LOCK);
1299         *num_processed = 0;
1300         return 0;
1301     }
1302 
1303     for (i = 0; i < num_msg; ++i) {
1304         m = &BIO_MSG_N(msg, i);
1305         l = dgram_pair_write_actual(bio, m->data, m->data_len,
1306                                     m->local, m->peer, 1);
1307         if (l < 0) {
1308             *num_processed = i;
1309             if (i > 0) {
1310                 ret = 1;
1311             } else {
1312                 ERR_raise(ERR_LIB_BIO, -l);
1313                 ret = 0;
1314             }
1315             goto out;
1316         }
1317 
1318         m->flags = 0;
1319     }
1320 
1321     *num_processed = i;
1322     ret = 1;
1323 out:
1324     CRYPTO_THREAD_unlock(b->lock);
1325     return ret;
1326 }
1327 
1328 #endif
1329