2 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
4 * Author: Jonathan Bastien-Filiatrault
6 * This file is part of GNUTLS.
8 * The GNUTLS library 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.
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.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include "gnutls_mbuffers.h"
24 #include "gnutls_errors.h"
26 /* Here be mbuffers */
28 /* A note on terminology:
30 * Variables named bufel designate a single buffer segment (mbuffer_st
31 * type). This type is textually referred to as a "segment" or a
34 * Variables named buf desigate a chain of buffer segments
35 * (mbuffer_head_st type). This type is textually referred to as a
36 * "buffer head" or simply as "buffer".
40 * - Make existing code easier to understand.
41 * - Make common operations more efficient by avoiding unnecessary
43 * - Provide a common datatype with a well-known interface to move
44 * data around and through the multiple protocol layers.
45 * - Enable a future implementation of DTLS, which needs the concept
46 * of record boundaries.
50 /* Initialize a buffer head.
54 void _mbuffer_head_init(mbuffer_head_st * buf)
63 /* Deallocate all buffer segments and reset the buffer head.
66 * n: Number of segments currently in the buffer.
68 void _mbuffer_head_clear(mbuffer_head_st * buf)
70 mbuffer_st *bufel, *next;
72 for (bufel = buf->head; bufel != NULL; bufel = next) {
77 _mbuffer_head_init(buf);
80 /* Append a segment to the end of this buffer.
84 void _mbuffer_enqueue(mbuffer_head_st * buf, mbuffer_st * bufel)
87 bufel->prev = buf->tail;
90 buf->byte_length += bufel->msg.size - bufel->mark;
92 if (buf->tail != NULL)
93 buf->tail->next = bufel;
99 /* Remove a segment from the buffer.
103 * Returns the buffer following it.
105 mbuffer_st *_mbuffer_dequeue(mbuffer_head_st * buf, mbuffer_st * bufel)
107 mbuffer_st *ret = bufel->next;
109 if (buf->tail == bufel) /* if last */
110 buf->tail = bufel->prev;
112 if (buf->head == bufel) /* if first */
113 buf->head = bufel->next;
116 bufel->prev->next = bufel->next;
119 bufel->next->prev = NULL;
122 buf->byte_length -= bufel->msg.size - bufel->mark;
124 bufel->next = bufel->prev = NULL;
129 /* Get a reference to the first segment of the buffer and
130 * remove it from the list.
132 * Used to start iteration.
136 mbuffer_st *_mbuffer_head_pop_first(mbuffer_head_st * buf)
138 mbuffer_st *bufel = buf->head;
140 if (buf->head == NULL)
143 _mbuffer_dequeue(buf, bufel);
148 /* Get a reference to the first segment of the buffer and its data.
150 * Used to start iteration or to peek at the data.
154 mbuffer_st *_mbuffer_head_get_first(mbuffer_head_st * buf,
155 gnutls_datum_t * msg)
157 mbuffer_st *bufel = buf->head;
161 msg->data = bufel->msg.data + bufel->mark;
162 msg->size = bufel->msg.size - bufel->mark;
171 /* Get a reference to the next segment of the buffer and its data.
173 * Used to iterate over the buffer segments.
177 mbuffer_st *_mbuffer_head_get_next(mbuffer_st * cur, gnutls_datum_t * msg)
179 mbuffer_st *bufel = cur->next;
183 msg->data = bufel->msg.data + bufel->mark;
184 msg->size = bufel->msg.size - bufel->mark;
193 /* Remove the first segment from the buffer.
195 * Used to dequeue data from the buffer. Not yet exposed in the
196 * internal interface since it is not yet needed outside of this unit.
200 static inline void remove_front(mbuffer_head_st * buf)
202 mbuffer_st *bufel = buf->head;
207 _mbuffer_dequeue(buf, bufel);
211 /* Remove a specified number of bytes from the start of the buffer.
213 * Useful for uses that treat the buffer as a simple array of bytes.
215 * If more than one mbuffer_st have been removed it
216 * returns 1, 0 otherwise and an error code on error.
219 * n: Number of segments needed to remove the specified amount of data.
221 int _mbuffer_head_remove_bytes(mbuffer_head_st * buf, size_t bytes)
224 mbuffer_st *bufel, *next;
227 if (bytes > buf->byte_length) {
229 return GNUTLS_E_INVALID_REQUEST;
232 for (bufel = buf->head; bufel != NULL && left > 0; bufel = next) {
235 if (left >= (bufel->msg.size - bufel->mark)) {
236 left -= (bufel->msg.size - bufel->mark);
241 buf->byte_length -= left;
248 /* Allocate a buffer segment. The segment is not initially "owned" by
251 * maximum_size: Amount of data that this segment can contain.
253 * Returns the segment or NULL on error.
257 mbuffer_st *_mbuffer_alloc(size_t maximum_size)
261 st = gnutls_malloc(maximum_size + sizeof(mbuffer_st));
267 /* set the structure to zero */
268 memset(st, 0, sizeof(*st));
270 /* payload points after the mbuffer_st structure */
271 st->msg.data = (uint8_t *) st + sizeof(mbuffer_st);
273 st->maximum_size = maximum_size;
279 /* Copy data into a segment. The segment must not be part of a buffer
280 * head when using this function.
282 * Bounds checking is performed by this function.
284 * Returns 0 on success or an error code otherwise.
287 * n: number of bytes to copy
290 _mbuffer_append_data(mbuffer_st * bufel, void *newdata,
293 if (bufel->msg.size + newdata_size <= bufel->maximum_size) {
294 memcpy(&bufel->msg.data[bufel->msg.size], newdata,
296 bufel->msg.size += newdata_size;
299 return GNUTLS_E_INVALID_REQUEST;
305 #ifdef ENABLE_ALIGN16
306 # define ALIGN_SIZE 16
308 /* Allocate a 16-byte alligned buffer segment. The segment is not initially "owned" by
311 * maximum_size: Amount of data that this segment can contain.
312 * align_pos: identifies the position of the buffer that will be aligned at 16-bytes
314 * This function should be used to ensure that encrypted data or data to
315 * be encrypted are properly aligned.
317 * Returns the segment or NULL on error.
321 mbuffer_st *_mbuffer_alloc_align16(size_t maximum_size, unsigned align_pos)
324 size_t cur_alignment;
326 st = gnutls_malloc(maximum_size + sizeof(mbuffer_st) + ALIGN_SIZE);
332 /* set the structure to zero */
333 memset(st, 0, sizeof(*st));
335 /* payload points after the mbuffer_st structure */
336 st->msg.data = (uint8_t *) st + sizeof(mbuffer_st);
338 cur_alignment = ((size_t)(st->msg.data+align_pos)) % ALIGN_SIZE;
339 if (cur_alignment > 0)
340 st->msg.data += ALIGN_SIZE - cur_alignment;
343 st->maximum_size = maximum_size;
348 static unsigned is_aligned16(mbuffer_st * bufel, unsigned align_pos)
350 uint8_t * ptr = _mbuffer_get_udata_ptr(bufel);
352 if (((size_t)(ptr+align_pos)) % ALIGN_SIZE == 0)
358 /* Takes a buffer in multiple chunks and puts all the data in a single
359 * contiguous segment, ensuring that the @align_pos is 16-byte aligned.
361 * Returns 0 on success or an error code otherwise.
364 * n: number of segments initially in the buffer
366 int _mbuffer_linearize_align16(mbuffer_head_st * buf, unsigned align_pos)
368 mbuffer_st *bufel, *cur;
372 if (buf->length == 0) {
377 bufel = _mbuffer_head_get_first(buf, NULL);
378 if (buf->length == 1 && is_aligned16(bufel, align_pos))
381 bufel = _mbuffer_alloc_align16(buf->byte_length, align_pos);
384 return GNUTLS_E_MEMORY_ERROR;
387 for (cur = _mbuffer_head_get_first(buf, &msg);
388 msg.data != NULL; cur = _mbuffer_head_get_next(cur, &msg)) {
389 memcpy(&bufel->msg.data[pos], msg.data, msg.size);
390 bufel->msg.size += msg.size;
394 _mbuffer_head_clear(buf);
395 _mbuffer_enqueue(buf, bufel);
400 int _mbuffer_linearize(mbuffer_head_st * buf)
402 mbuffer_st *bufel, *cur;
406 if (buf->length <= 1) {
411 bufel = _mbuffer_alloc(buf->byte_length);
414 return GNUTLS_E_MEMORY_ERROR;
417 for (cur = _mbuffer_head_get_first(buf, &msg);
418 msg.data != NULL; cur = _mbuffer_head_get_next(cur, &msg)) {
419 memcpy(&bufel->msg.data[pos], msg.data, msg.size);
420 bufel->msg.size += msg.size;
424 _mbuffer_head_clear(buf);
425 _mbuffer_enqueue(buf, bufel);