Line data Source code
1 : /* 2 : * Copyright (C) 2019 Red Hat, Inc. 3 : * 4 : * Author: Daiki Ueno 5 : * 6 : * This file is part of GnuTLS. 7 : * 8 : * The GnuTLS is free software; you can redistribute it and/or 9 : * modify it under the terms of the GNU Lesser General Public License 10 : * as published by the Free Software Foundation; either version 2.1 of 11 : * the License, or (at your option) any later version. 12 : * 13 : * This library is distributed in the hope that it will be useful, but 14 : * WITHOUT ANY WARRANTY; without even the implied warranty of 15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 : * Lesser General Public License for more details. 17 : * 18 : * You should have received a copy of the GNU Lesser General Public License 19 : * along with this program. If not, see <https://www.gnu.org/licenses/> 20 : * 21 : */ 22 : 23 : #include "gnutls_int.h" 24 : #include "iov.h" 25 : 26 : /** 27 : * _gnutls_iov_iter_init: 28 : * @iter: the iterator 29 : * @iov: the data buffers 30 : * @iov_count: the number of data buffers 31 : * @block_size: block size to iterate 32 : * 33 : * Initialize the iterator. 34 : * 35 : * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise 36 : * an error code is returned 37 : */ 38 : int 39 837583 : _gnutls_iov_iter_init(struct iov_iter_st *iter, 40 : const giovec_t *iov, size_t iov_count, 41 : size_t block_size) 42 : { 43 837583 : if (unlikely(block_size > MAX_CIPHER_BLOCK_SIZE)) 44 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); 45 : 46 837583 : iter->iov = iov; 47 837583 : iter->iov_count = iov_count; 48 837583 : iter->iov_index = 0; 49 837583 : iter->iov_offset = 0; 50 837583 : iter->block_size = block_size; 51 837583 : iter->block_offset = 0; 52 837583 : return 0; 53 : } 54 : 55 : /** 56 : * _gnutls_iov_iter_next: 57 : * @iter: the iterator 58 : * @data: the return location of extracted data 59 : * 60 : * Retrieve block(s) pointed by @iter and advance it to the next 61 : * position. It returns the number of bytes in @data. At the end of 62 : * iteration, 0 is returned. 63 : * 64 : * If the data stored in @iter is not multiple of the block size, the 65 : * remaining data is stored in the "block" field of @iter with the 66 : * size stored in the "block_offset" field. 67 : * 68 : * Returns: On success, a value greater than or equal to zero is 69 : * returned, otherwise a negative error code is returned 70 : */ 71 : ssize_t 72 1902230 : _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data) 73 : { 74 3136710 : while (iter->iov_index < iter->iov_count) { 75 1471160 : const giovec_t *iov = &iter->iov[iter->iov_index]; 76 1471160 : uint8_t *p = iov->iov_base; 77 1471160 : size_t len = iov->iov_len; 78 1471160 : size_t block_left; 79 : 80 1471160 : if (!p) { 81 : // skip NULL iov entries, else we run into issues below 82 32 : iter->iov_index++; 83 32 : continue; 84 : } 85 : 86 1471130 : if (unlikely(len < iter->iov_offset)) 87 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); 88 1471130 : len -= iter->iov_offset; 89 1471130 : p += iter->iov_offset; 90 : 91 : /* We have at least one full block, return a whole set 92 : * of full blocks immediately. */ 93 1471130 : if (iter->block_offset == 0 && len >= iter->block_size) { 94 227066 : if ((len % iter->block_size) == 0) { 95 12491 : iter->iov_index++; 96 12491 : iter->iov_offset = 0; 97 : } else { 98 214575 : len -= (len % iter->block_size); 99 214575 : iter->iov_offset += len; 100 : } 101 : 102 : /* Return the blocks. */ 103 227066 : *data = p; 104 227066 : return len; 105 : } 106 : 107 : /* We can complete one full block to return. */ 108 1244060 : block_left = iter->block_size - iter->block_offset; 109 1244060 : if (len >= block_left) { 110 9613 : memcpy(iter->block + iter->block_offset, p, block_left); 111 9613 : if (len == block_left) { 112 9576 : iter->iov_index++; 113 9576 : iter->iov_offset = 0; 114 : } else 115 37 : iter->iov_offset += block_left; 116 9613 : iter->block_offset = 0; 117 : 118 : /* Return the filled block. */ 119 9613 : *data = iter->block; 120 9613 : return iter->block_size; 121 : } 122 : 123 : /* Not enough data for a full block, store in temp 124 : * memory and continue. */ 125 1234450 : memcpy(iter->block + iter->block_offset, p, len); 126 1234450 : iter->block_offset += len; 127 1234450 : iter->iov_index++; 128 1234450 : iter->iov_offset = 0; 129 : } 130 : 131 1665550 : if (iter->block_offset > 0) { 132 827968 : size_t len = iter->block_offset; 133 : 134 : /* Return the incomplete block. */ 135 827968 : *data = iter->block; 136 827968 : iter->block_offset = 0; 137 827968 : return len; 138 : } 139 : 140 : return 0; 141 : } 142 : 143 : /** 144 : * _gnutls_iov_iter_sync: 145 : * @iter: the iterator 146 : * @data: data returned by _gnutls_iov_iter_next 147 : * @data_size: size of @data 148 : * 149 : * Flush the content of temp buffer (if any) to the data buffer. 150 : */ 151 : int 152 56 : _gnutls_iov_iter_sync(struct iov_iter_st *iter, const uint8_t *data, 153 : size_t data_size) 154 : { 155 56 : size_t iov_index; 156 56 : size_t iov_offset; 157 : 158 : /* We didn't return the cached block. */ 159 56 : if (data != iter->block) 160 : return 0; 161 : 162 21 : iov_index = iter->iov_index; 163 21 : iov_offset = iter->iov_offset; 164 : 165 : /* When syncing a cache block we walk backwards because we only have a 166 : * pointer to were the block ends in the iovec, walking backwards is 167 : * fine as we are always writing a full block, so the whole content 168 : * is written in the right places: 169 : * iovec: |--0--|---1---|--2--|-3-| 170 : * block: |-----------------------| 171 : * 1st write |---| 172 : * 2nd write |----- 173 : * 3rd write |------- 174 : * last write |----- 175 : */ 176 59 : while (data_size > 0) { 177 : const giovec_t *iov; 178 : uint8_t *p; 179 : size_t to_write; 180 : 181 73 : while (iov_offset == 0) { 182 35 : if (unlikely(iov_index == 0)) 183 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); 184 : 185 35 : iov_index--; 186 35 : iov_offset = iter->iov[iov_index].iov_len; 187 : } 188 : 189 38 : iov = &iter->iov[iov_index]; 190 38 : p = iov->iov_base; 191 38 : to_write = MIN(data_size, iov_offset); 192 : 193 38 : iov_offset -= to_write; 194 38 : data_size -= to_write; 195 : 196 38 : memcpy(p + iov_offset, &iter->block[data_size], to_write); 197 : } 198 : 199 : return 0; 200 : }