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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 15 * Use is subject to license terms. 16 */ 17 18 /* $OpenBSD: buffer.c,v 1.31 2006/08/03 03:34:41 deraadt Exp $ */ 19 20 #include "includes.h" 21 22 #include "xmalloc.h" 23 #include "buffer.h" 24 #include "log.h" 25 26 #define BUFFER_MAX_CHUNK 0x100000 27 #define BUFFER_MAX_LEN 0xa00000 28 #define BUFFER_ALLOCSZ 0x008000 29 30 /* Initializes the buffer structure. */ 31 32 void 33 buffer_init(Buffer *buffer) 34 { 35 const u_int len = 4096; 36 37 buffer->alloc = 0; 38 buffer->buf = xmalloc(len); 39 buffer->alloc = len; 40 buffer->offset = 0; 41 buffer->end = 0; 42 } 43 44 /* Frees any memory used for the buffer. */ 45 46 void 47 buffer_free(Buffer *buffer) 48 { 49 if (buffer->alloc > 0) { 50 memset(buffer->buf, 0, buffer->alloc); 51 buffer->alloc = 0; 52 xfree(buffer->buf); 53 } 54 } 55 56 /* 57 * Clears any data from the buffer, making it empty. This does not actually 58 * zero the memory. 59 */ 60 61 void 62 buffer_clear(Buffer *buffer) 63 { 64 buffer->offset = 0; 65 buffer->end = 0; 66 } 67 68 /* Appends data to the buffer, expanding it if necessary. */ 69 70 void 71 buffer_append(Buffer *buffer, const void *data, u_int len) 72 { 73 void *p; 74 p = buffer_append_space(buffer, len); 75 memcpy(p, data, len); 76 } 77 78 static int 79 buffer_compact(Buffer *buffer) 80 { 81 /* 82 * If the buffer is quite empty, but all data is at the end, move the 83 * data to the beginning. 84 */ 85 if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { 86 memmove(buffer->buf, buffer->buf + buffer->offset, 87 buffer->end - buffer->offset); 88 buffer->end -= buffer->offset; 89 buffer->offset = 0; 90 return (1); 91 } 92 return (0); 93 } 94 95 /* 96 * Appends space to the buffer, expanding the buffer if necessary. This does 97 * not actually copy the data into the buffer, but instead returns a pointer 98 * to the allocated region. 99 */ 100 101 void * 102 buffer_append_space(Buffer *buffer, u_int len) 103 { 104 u_int newlen; 105 void *p; 106 107 if (len > BUFFER_MAX_CHUNK) 108 fatal("buffer_append_space: len %u not supported", len); 109 110 /* If the buffer is empty, start using it from the beginning. */ 111 if (buffer->offset == buffer->end) { 112 buffer->offset = 0; 113 buffer->end = 0; 114 } 115 restart: 116 /* If there is enough space to store all data, store it now. */ 117 if (buffer->end + len < buffer->alloc) { 118 p = buffer->buf + buffer->end; 119 buffer->end += len; 120 return p; 121 } 122 123 /* Compact data back to the start of the buffer if necessary */ 124 if (buffer_compact(buffer)) 125 goto restart; 126 127 /* Increase the size of the buffer and retry. */ 128 newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); 129 if (newlen > BUFFER_MAX_LEN) 130 fatal("buffer_append_space: alloc %u not supported", 131 newlen); 132 buffer->buf = xrealloc(buffer->buf, newlen); 133 buffer->alloc = newlen; 134 goto restart; 135 /* NOTREACHED */ 136 } 137 138 /* 139 * Check whether an allocation of 'len' will fit in the buffer 140 * This must follow the same math as buffer_append_space 141 */ 142 int 143 buffer_check_alloc(Buffer *buffer, u_int len) 144 { 145 if (buffer->offset == buffer->end) { 146 buffer->offset = 0; 147 buffer->end = 0; 148 } 149 restart: 150 if (buffer->end + len < buffer->alloc) 151 return (1); 152 if (buffer_compact(buffer)) 153 goto restart; 154 if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) 155 return (1); 156 return (0); 157 } 158 159 /* Returns the number of bytes of data in the buffer. */ 160 161 u_int 162 buffer_len(Buffer *buffer) 163 { 164 return buffer->end - buffer->offset; 165 } 166 167 /* Gets data from the beginning of the buffer. */ 168 169 int 170 buffer_get_ret(Buffer *buffer, void *buf, u_int len) 171 { 172 if (len > buffer->end - buffer->offset) { 173 error("buffer_get_ret: trying to get more bytes %d than in buffer %d", 174 len, buffer->end - buffer->offset); 175 return (-1); 176 } 177 memcpy(buf, buffer->buf + buffer->offset, len); 178 buffer->offset += len; 179 return (0); 180 } 181 182 void 183 buffer_get(Buffer *buffer, void *buf, u_int len) 184 { 185 if (buffer_get_ret(buffer, buf, len) == -1) 186 fatal("buffer_get: buffer error"); 187 } 188 189 /* Consumes the given number of bytes from the beginning of the buffer. */ 190 191 int 192 buffer_consume_ret(Buffer *buffer, u_int bytes) 193 { 194 if (bytes > buffer->end - buffer->offset) { 195 error("buffer_consume_ret: trying to get more bytes than in buffer"); 196 return (-1); 197 } 198 buffer->offset += bytes; 199 return (0); 200 } 201 202 void 203 buffer_consume(Buffer *buffer, u_int bytes) 204 { 205 if (buffer_consume_ret(buffer, bytes) == -1) 206 fatal("buffer_consume: buffer error"); 207 } 208 209 /* Consumes the given number of bytes from the end of the buffer. */ 210 211 int 212 buffer_consume_end_ret(Buffer *buffer, u_int bytes) 213 { 214 if (bytes > buffer->end - buffer->offset) 215 return (-1); 216 buffer->end -= bytes; 217 return (0); 218 } 219 220 void 221 buffer_consume_end(Buffer *buffer, u_int bytes) 222 { 223 if (buffer_consume_end_ret(buffer, bytes) == -1) 224 fatal("buffer_consume_end: trying to get more bytes than in buffer"); 225 } 226 227 /* Returns a pointer to the first used byte in the buffer. */ 228 229 void * 230 buffer_ptr(Buffer *buffer) 231 { 232 return buffer->buf + buffer->offset; 233 } 234 235 /* Dumps the contents of the buffer to stderr. */ 236 void 237 buffer_dump(Buffer *buffer) 238 { 239 u_int i; 240 u_char *ucp = buffer->buf; 241 242 for (i = buffer->offset; i < buffer->end; i++) { 243 fprintf(stderr, "%02x", ucp[i]); 244 if ((i-buffer->offset)%16==15) 245 fprintf(stderr, "\n"); 246 else if ((i-buffer->offset)%2==1) 247 fprintf(stderr, " "); 248 } 249 250 if (buffer->offset == buffer->end) { 251 /* explicitly state when the buffer is empty */ 252 fprintf(stderr, "<EMPTY BUFFER>\n"); 253 } else { 254 /* print the terminal '\n' if it wasn't already printed */ 255 if ((i - buffer->offset) % 16 != 0) 256 fprintf(stderr, "\n"); 257 } 258 /* 259 * We want an extra empty line after the packet dump for better 260 * readability. 261 */ 262 fprintf(stderr, "\n"); 263 }