1 /* 2 * Copyright 2023-2025 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 <openssl/bio.h> 11 #include "quictestlib.h" 12 #include "../testutil.h" 13 14 static long pkt_split_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr) 15 { 16 long ret; 17 BIO *next = BIO_next(bio); 18 19 if (next == NULL) 20 return 0; 21 22 switch (cmd) { 23 case BIO_CTRL_DUP: 24 ret = 0L; 25 break; 26 default: 27 ret = BIO_ctrl(next, cmd, num, ptr); 28 break; 29 } 30 return ret; 31 } 32 33 static int pkt_split_dgram_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride, 34 size_t num_msg, uint64_t flags, 35 size_t *msgs_processed) 36 { 37 BIO *next = BIO_next(bio); 38 39 if (next == NULL) 40 return 0; 41 42 /* 43 * We only introduce noise when receiving messages. We just pass this on 44 * to the underlying BIO. 45 */ 46 return BIO_sendmmsg(next, msg, stride, num_msg, flags, msgs_processed); 47 } 48 49 static int pkt_split_dgram_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride, 50 size_t num_msg, uint64_t flags, 51 size_t *msgs_processed) 52 { 53 BIO *next = BIO_next(bio); 54 size_t i, j, data_len = 0, msg_cnt = 0; 55 BIO_MSG *thismsg; 56 QTEST_DATA *bdata = BIO_get_data(bio); 57 58 if (!TEST_ptr(next) || !TEST_ptr(bdata)) 59 return 0; 60 61 /* 62 * For simplicity we assume that all elements in the msg array have the 63 * same data_len. They are not required to by the API, but it would be quite 64 * strange for that not to be the case - and our code that calls 65 * BIO_recvmmsg does do this (which is all that is important for this test 66 * code). We test the invariant here. 67 */ 68 for (i = 0; i < num_msg; i++) { 69 if (i == 0) 70 data_len = msg[i].data_len; 71 else if (!TEST_size_t_eq(msg[i].data_len, data_len)) 72 return 0; 73 } 74 75 if (!BIO_recvmmsg(next, msg, stride, num_msg, flags, msgs_processed)) 76 return 0; 77 78 msg_cnt = *msgs_processed; 79 if (msg_cnt == num_msg) 80 return 1; /* We've used all our slots and can't split any more */ 81 assert(msg_cnt < num_msg); 82 83 for (i = 0, thismsg = msg; i < msg_cnt; i++, thismsg++) { 84 QUIC_PKT_HDR hdr; 85 PACKET pkt; 86 size_t remain; 87 88 if (!PACKET_buf_init(&pkt, thismsg->data, thismsg->data_len)) 89 return 0; 90 91 /* Decode the packet header */ 92 if (ossl_quic_wire_decode_pkt_hdr(&pkt, bdata->short_conn_id_len, 93 0, 0, &hdr, NULL, NULL) != 1) 94 return 0; 95 remain = PACKET_remaining(&pkt); 96 if (remain > 0) { 97 for (j = msg_cnt; j > i; j--) { 98 if (!bio_msg_copy(&msg[j], &msg[j - 1])) 99 return 0; 100 } 101 thismsg->data_len -= remain; 102 msg[i + 1].data_len = remain; 103 memmove(msg[i + 1].data, 104 (unsigned char *)msg[i + 1].data + thismsg->data_len, 105 remain); 106 msg_cnt++; 107 } 108 } 109 110 *msgs_processed = msg_cnt; 111 return 1; 112 } 113 114 /* Choose a sufficiently large type likely to be unused for this custom BIO */ 115 #define BIO_TYPE_PKT_SPLIT_DGRAM_FILTER (0x81 | BIO_TYPE_FILTER) 116 117 static BIO_METHOD *method_pkt_split_dgram = NULL; 118 119 /* Note: Not thread safe! */ 120 const BIO_METHOD *bio_f_pkt_split_dgram_filter(void) 121 { 122 if (method_pkt_split_dgram == NULL) { 123 method_pkt_split_dgram = BIO_meth_new(BIO_TYPE_PKT_SPLIT_DGRAM_FILTER, 124 "Packet splitting datagram filter"); 125 if (method_pkt_split_dgram == NULL 126 || !BIO_meth_set_ctrl(method_pkt_split_dgram, pkt_split_dgram_ctrl) 127 || !BIO_meth_set_sendmmsg(method_pkt_split_dgram, 128 pkt_split_dgram_sendmmsg) 129 || !BIO_meth_set_recvmmsg(method_pkt_split_dgram, 130 pkt_split_dgram_recvmmsg)) 131 return NULL; 132 } 133 return method_pkt_split_dgram; 134 } 135 136 void bio_f_pkt_split_dgram_filter_free(void) 137 { 138 BIO_meth_free(method_pkt_split_dgram); 139 } 140