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