buffer.c revision 294693
1/* $OpenBSD: buffer.c,v 1.35 2014/02/02 03:44:31 djm Exp $ */ 2/* 3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5 * All rights reserved 6 * Functions for manipulating fifo buffers (that can grow if needed). 7 * 8 * As far as I am concerned, the code I have written for this software 9 * can be used freely for any purpose. Any derived versions of this 10 * software must be clearly marked as such, and if the derived work is 11 * incompatible with the protocol description in the RFC file, it must be 12 * called by a name other than "ssh" or "Secure Shell". 13 */ 14 15#include "includes.h" 16 17#include <sys/param.h> 18 19#include <stdio.h> 20#include <string.h> 21#include <stdarg.h> 22#include <stdlib.h> 23 24#include "xmalloc.h" 25#include "buffer.h" 26#include "log.h" 27 28#define BUFFER_MAX_CHUNK 0x100000 29#define BUFFER_MAX_LEN 0xa00000 30#define BUFFER_ALLOCSZ 0x008000 31 32/* Initializes the buffer structure. */ 33 34void 35buffer_init(Buffer *buffer) 36{ 37 const u_int len = 4096; 38 39 buffer->alloc = 0; 40 buffer->buf = xmalloc(len); 41 buffer->alloc = len; 42 buffer->offset = 0; 43 buffer->end = 0; 44} 45 46/* Frees any memory used for the buffer. */ 47 48void 49buffer_free(Buffer *buffer) 50{ 51 if (buffer->alloc > 0) { 52 explicit_bzero(buffer->buf, buffer->alloc); 53 buffer->alloc = 0; 54 free(buffer->buf); 55 } 56} 57 58/* 59 * Clears any data from the buffer, making it empty. This does not actually 60 * zero the memory. 61 */ 62 63void 64buffer_clear(Buffer *buffer) 65{ 66 buffer->offset = 0; 67 buffer->end = 0; 68} 69 70/* Appends data to the buffer, expanding it if necessary. */ 71 72void 73buffer_append(Buffer *buffer, const void *data, u_int len) 74{ 75 void *p; 76 p = buffer_append_space(buffer, len); 77 memcpy(p, data, len); 78} 79 80static int 81buffer_compact(Buffer *buffer) 82{ 83 /* 84 * If the buffer is quite empty, but all data is at the end, move the 85 * data to the beginning. 86 */ 87 if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { 88 memmove(buffer->buf, buffer->buf + buffer->offset, 89 buffer->end - buffer->offset); 90 buffer->end -= buffer->offset; 91 buffer->offset = 0; 92 return (1); 93 } 94 return (0); 95} 96 97/* 98 * Appends space to the buffer, expanding the buffer if necessary. This does 99 * not actually copy the data into the buffer, but instead returns a pointer 100 * to the allocated region. 101 */ 102 103void * 104buffer_append_space(Buffer *buffer, u_int len) 105{ 106 u_int newlen; 107 void *p; 108 109 if (len > BUFFER_MAX_CHUNK) 110 fatal("buffer_append_space: len %u not supported", len); 111 112 /* If the buffer is empty, start using it from the beginning. */ 113 if (buffer->offset == buffer->end) { 114 buffer->offset = 0; 115 buffer->end = 0; 116 } 117restart: 118 /* If there is enough space to store all data, store it now. */ 119 if (buffer->end + len < buffer->alloc) { 120 p = buffer->buf + buffer->end; 121 buffer->end += len; 122 return p; 123 } 124 125 /* Compact data back to the start of the buffer if necessary */ 126 if (buffer_compact(buffer)) 127 goto restart; 128 129 /* Increase the size of the buffer and retry. */ 130 newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); 131 if (newlen > BUFFER_MAX_LEN) 132 fatal("buffer_append_space: alloc %u not supported", 133 newlen); 134 buffer->buf = xrealloc(buffer->buf, 1, newlen); 135 buffer->alloc = newlen; 136 goto restart; 137 /* NOTREACHED */ 138} 139 140/* 141 * Check whether an allocation of 'len' will fit in the buffer 142 * This must follow the same math as buffer_append_space 143 */ 144int 145buffer_check_alloc(Buffer *buffer, u_int len) 146{ 147 if (buffer->offset == buffer->end) { 148 buffer->offset = 0; 149 buffer->end = 0; 150 } 151 restart: 152 if (buffer->end + len < buffer->alloc) 153 return (1); 154 if (buffer_compact(buffer)) 155 goto restart; 156 if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) 157 return (1); 158 return (0); 159} 160 161/* Returns the number of bytes of data in the buffer. */ 162 163u_int 164buffer_len(const Buffer *buffer) 165{ 166 return buffer->end - buffer->offset; 167} 168 169/* Gets data from the beginning of the buffer. */ 170 171int 172buffer_get_ret(Buffer *buffer, void *buf, u_int len) 173{ 174 if (len > buffer->end - buffer->offset) { 175 error("buffer_get_ret: trying to get more bytes %d than in buffer %d", 176 len, buffer->end - buffer->offset); 177 return (-1); 178 } 179 memcpy(buf, buffer->buf + buffer->offset, len); 180 buffer->offset += len; 181 return (0); 182} 183 184void 185buffer_get(Buffer *buffer, void *buf, u_int len) 186{ 187 if (buffer_get_ret(buffer, buf, len) == -1) 188 fatal("buffer_get: buffer error"); 189} 190 191/* Consumes the given number of bytes from the beginning of the buffer. */ 192 193int 194buffer_consume_ret(Buffer *buffer, u_int bytes) 195{ 196 if (bytes > buffer->end - buffer->offset) { 197 error("buffer_consume_ret: trying to get more bytes than in buffer"); 198 return (-1); 199 } 200 buffer->offset += bytes; 201 return (0); 202} 203 204void 205buffer_consume(Buffer *buffer, u_int bytes) 206{ 207 if (buffer_consume_ret(buffer, bytes) == -1) 208 fatal("buffer_consume: buffer error"); 209} 210 211/* Consumes the given number of bytes from the end of the buffer. */ 212 213int 214buffer_consume_end_ret(Buffer *buffer, u_int bytes) 215{ 216 if (bytes > buffer->end - buffer->offset) 217 return (-1); 218 buffer->end -= bytes; 219 return (0); 220} 221 222void 223buffer_consume_end(Buffer *buffer, u_int bytes) 224{ 225 if (buffer_consume_end_ret(buffer, bytes) == -1) 226 fatal("buffer_consume_end: trying to get more bytes than in buffer"); 227} 228 229/* Returns a pointer to the first used byte in the buffer. */ 230 231void * 232buffer_ptr(const Buffer *buffer) 233{ 234 return buffer->buf + buffer->offset; 235} 236 237/* Dumps the contents of the buffer to stderr. */ 238 239void 240buffer_dump(const Buffer *buffer) 241{ 242 u_int i; 243 u_char *ucp = buffer->buf; 244 245 for (i = buffer->offset; i < buffer->end; i++) { 246 fprintf(stderr, "%02x", ucp[i]); 247 if ((i-buffer->offset)%16==15) 248 fprintf(stderr, "\r\n"); 249 else if ((i-buffer->offset)%2==1) 250 fprintf(stderr, " "); 251 } 252 fprintf(stderr, "\r\n"); 253} 254