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