10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi> 30Sstevel@tonic-gate * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 40Sstevel@tonic-gate * All rights reserved 50Sstevel@tonic-gate * Functions for manipulating fifo buffers (that can grow if needed). 60Sstevel@tonic-gate * 70Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software 80Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this 90Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is 100Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be 110Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell". 120Sstevel@tonic-gate */ 13*7574SJan.Pechanec@Sun.COM /* 14*7574SJan.Pechanec@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 15*7574SJan.Pechanec@Sun.COM * Use is subject to license terms. 16*7574SJan.Pechanec@Sun.COM */ 170Sstevel@tonic-gate 185087Sjp161948 /* $OpenBSD: buffer.c,v 1.31 2006/08/03 03:34:41 deraadt Exp $ */ 190Sstevel@tonic-gate 205087Sjp161948 #include "includes.h" 215087Sjp161948 220Sstevel@tonic-gate #include "xmalloc.h" 230Sstevel@tonic-gate #include "buffer.h" 240Sstevel@tonic-gate #include "log.h" 250Sstevel@tonic-gate 265087Sjp161948 #define BUFFER_MAX_CHUNK 0x100000 275087Sjp161948 #define BUFFER_MAX_LEN 0xa00000 285087Sjp161948 #define BUFFER_ALLOCSZ 0x008000 295087Sjp161948 300Sstevel@tonic-gate /* Initializes the buffer structure. */ 310Sstevel@tonic-gate 320Sstevel@tonic-gate void 330Sstevel@tonic-gate buffer_init(Buffer *buffer) 340Sstevel@tonic-gate { 350Sstevel@tonic-gate const u_int len = 4096; 360Sstevel@tonic-gate 370Sstevel@tonic-gate buffer->alloc = 0; 380Sstevel@tonic-gate buffer->buf = xmalloc(len); 390Sstevel@tonic-gate buffer->alloc = len; 400Sstevel@tonic-gate buffer->offset = 0; 410Sstevel@tonic-gate buffer->end = 0; 420Sstevel@tonic-gate } 430Sstevel@tonic-gate 440Sstevel@tonic-gate /* Frees any memory used for the buffer. */ 450Sstevel@tonic-gate 460Sstevel@tonic-gate void 470Sstevel@tonic-gate buffer_free(Buffer *buffer) 480Sstevel@tonic-gate { 490Sstevel@tonic-gate if (buffer->alloc > 0) { 500Sstevel@tonic-gate memset(buffer->buf, 0, buffer->alloc); 512757Sjp161948 buffer->alloc = 0; 520Sstevel@tonic-gate xfree(buffer->buf); 530Sstevel@tonic-gate } 540Sstevel@tonic-gate } 550Sstevel@tonic-gate 560Sstevel@tonic-gate /* 570Sstevel@tonic-gate * Clears any data from the buffer, making it empty. This does not actually 580Sstevel@tonic-gate * zero the memory. 590Sstevel@tonic-gate */ 600Sstevel@tonic-gate 610Sstevel@tonic-gate void 620Sstevel@tonic-gate buffer_clear(Buffer *buffer) 630Sstevel@tonic-gate { 640Sstevel@tonic-gate buffer->offset = 0; 650Sstevel@tonic-gate buffer->end = 0; 660Sstevel@tonic-gate } 670Sstevel@tonic-gate 680Sstevel@tonic-gate /* Appends data to the buffer, expanding it if necessary. */ 690Sstevel@tonic-gate 700Sstevel@tonic-gate void 710Sstevel@tonic-gate buffer_append(Buffer *buffer, const void *data, u_int len) 720Sstevel@tonic-gate { 730Sstevel@tonic-gate void *p; 740Sstevel@tonic-gate p = buffer_append_space(buffer, len); 750Sstevel@tonic-gate memcpy(p, data, len); 760Sstevel@tonic-gate } 770Sstevel@tonic-gate 785087Sjp161948 static int 795087Sjp161948 buffer_compact(Buffer *buffer) 805087Sjp161948 { 815087Sjp161948 /* 825087Sjp161948 * If the buffer is quite empty, but all data is at the end, move the 835087Sjp161948 * data to the beginning. 845087Sjp161948 */ 855087Sjp161948 if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { 865087Sjp161948 memmove(buffer->buf, buffer->buf + buffer->offset, 875087Sjp161948 buffer->end - buffer->offset); 885087Sjp161948 buffer->end -= buffer->offset; 895087Sjp161948 buffer->offset = 0; 905087Sjp161948 return (1); 915087Sjp161948 } 925087Sjp161948 return (0); 935087Sjp161948 } 945087Sjp161948 950Sstevel@tonic-gate /* 960Sstevel@tonic-gate * Appends space to the buffer, expanding the buffer if necessary. This does 970Sstevel@tonic-gate * not actually copy the data into the buffer, but instead returns a pointer 980Sstevel@tonic-gate * to the allocated region. 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate void * 1020Sstevel@tonic-gate buffer_append_space(Buffer *buffer, u_int len) 1030Sstevel@tonic-gate { 1040Sstevel@tonic-gate u_int newlen; 1050Sstevel@tonic-gate void *p; 1060Sstevel@tonic-gate 1072757Sjp161948 if (len > BUFFER_MAX_CHUNK) 1080Sstevel@tonic-gate fatal("buffer_append_space: len %u not supported", len); 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate /* If the buffer is empty, start using it from the beginning. */ 1110Sstevel@tonic-gate if (buffer->offset == buffer->end) { 1120Sstevel@tonic-gate buffer->offset = 0; 1130Sstevel@tonic-gate buffer->end = 0; 1140Sstevel@tonic-gate } 1150Sstevel@tonic-gate restart: 1160Sstevel@tonic-gate /* If there is enough space to store all data, store it now. */ 1170Sstevel@tonic-gate if (buffer->end + len < buffer->alloc) { 1180Sstevel@tonic-gate p = buffer->buf + buffer->end; 1190Sstevel@tonic-gate buffer->end += len; 1200Sstevel@tonic-gate return p; 1210Sstevel@tonic-gate } 1225087Sjp161948 1235087Sjp161948 /* Compact data back to the start of the buffer if necessary */ 1245087Sjp161948 if (buffer_compact(buffer)) 1250Sstevel@tonic-gate goto restart; 1265087Sjp161948 1270Sstevel@tonic-gate /* Increase the size of the buffer and retry. */ 1285087Sjp161948 newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); 1292757Sjp161948 if (newlen > BUFFER_MAX_LEN) 1300Sstevel@tonic-gate fatal("buffer_append_space: alloc %u not supported", 1310Sstevel@tonic-gate newlen); 1320Sstevel@tonic-gate buffer->buf = xrealloc(buffer->buf, newlen); 1330Sstevel@tonic-gate buffer->alloc = newlen; 1340Sstevel@tonic-gate goto restart; 1350Sstevel@tonic-gate /* NOTREACHED */ 1360Sstevel@tonic-gate } 1370Sstevel@tonic-gate 1385087Sjp161948 /* 1395087Sjp161948 * Check whether an allocation of 'len' will fit in the buffer 1405087Sjp161948 * This must follow the same math as buffer_append_space 1415087Sjp161948 */ 1425087Sjp161948 int 1435087Sjp161948 buffer_check_alloc(Buffer *buffer, u_int len) 1445087Sjp161948 { 1455087Sjp161948 if (buffer->offset == buffer->end) { 1465087Sjp161948 buffer->offset = 0; 1475087Sjp161948 buffer->end = 0; 1485087Sjp161948 } 1495087Sjp161948 restart: 1505087Sjp161948 if (buffer->end + len < buffer->alloc) 1515087Sjp161948 return (1); 1525087Sjp161948 if (buffer_compact(buffer)) 1535087Sjp161948 goto restart; 1545087Sjp161948 if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) 1555087Sjp161948 return (1); 1565087Sjp161948 return (0); 1575087Sjp161948 } 1585087Sjp161948 1590Sstevel@tonic-gate /* Returns the number of bytes of data in the buffer. */ 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate u_int 1620Sstevel@tonic-gate buffer_len(Buffer *buffer) 1630Sstevel@tonic-gate { 1640Sstevel@tonic-gate return buffer->end - buffer->offset; 1650Sstevel@tonic-gate } 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate /* Gets data from the beginning of the buffer. */ 1680Sstevel@tonic-gate 1692757Sjp161948 int 1702757Sjp161948 buffer_get_ret(Buffer *buffer, void *buf, u_int len) 1712757Sjp161948 { 1722757Sjp161948 if (len > buffer->end - buffer->offset) { 1732757Sjp161948 error("buffer_get_ret: trying to get more bytes %d than in buffer %d", 1742757Sjp161948 len, buffer->end - buffer->offset); 1752757Sjp161948 return (-1); 1762757Sjp161948 } 1772757Sjp161948 memcpy(buf, buffer->buf + buffer->offset, len); 1782757Sjp161948 buffer->offset += len; 1792757Sjp161948 return (0); 1802757Sjp161948 } 1812757Sjp161948 1820Sstevel@tonic-gate void 1830Sstevel@tonic-gate buffer_get(Buffer *buffer, void *buf, u_int len) 1840Sstevel@tonic-gate { 1852757Sjp161948 if (buffer_get_ret(buffer, buf, len) == -1) 1862757Sjp161948 fatal("buffer_get: buffer error"); 1870Sstevel@tonic-gate } 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate /* Consumes the given number of bytes from the beginning of the buffer. */ 1900Sstevel@tonic-gate 1912757Sjp161948 int 1922757Sjp161948 buffer_consume_ret(Buffer *buffer, u_int bytes) 1932757Sjp161948 { 1942757Sjp161948 if (bytes > buffer->end - buffer->offset) { 1952757Sjp161948 error("buffer_consume_ret: trying to get more bytes than in buffer"); 1962757Sjp161948 return (-1); 1972757Sjp161948 } 1982757Sjp161948 buffer->offset += bytes; 1992757Sjp161948 return (0); 2002757Sjp161948 } 2012757Sjp161948 2020Sstevel@tonic-gate void 2030Sstevel@tonic-gate buffer_consume(Buffer *buffer, u_int bytes) 2040Sstevel@tonic-gate { 2052757Sjp161948 if (buffer_consume_ret(buffer, bytes) == -1) 2062757Sjp161948 fatal("buffer_consume: buffer error"); 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate /* Consumes the given number of bytes from the end of the buffer. */ 2100Sstevel@tonic-gate 2112757Sjp161948 int 2122757Sjp161948 buffer_consume_end_ret(Buffer *buffer, u_int bytes) 2132757Sjp161948 { 2142757Sjp161948 if (bytes > buffer->end - buffer->offset) 2152757Sjp161948 return (-1); 2162757Sjp161948 buffer->end -= bytes; 2172757Sjp161948 return (0); 2182757Sjp161948 } 2192757Sjp161948 2200Sstevel@tonic-gate void 2210Sstevel@tonic-gate buffer_consume_end(Buffer *buffer, u_int bytes) 2220Sstevel@tonic-gate { 2232757Sjp161948 if (buffer_consume_end_ret(buffer, bytes) == -1) 2240Sstevel@tonic-gate fatal("buffer_consume_end: trying to get more bytes than in buffer"); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate /* Returns a pointer to the first used byte in the buffer. */ 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate void * 2300Sstevel@tonic-gate buffer_ptr(Buffer *buffer) 2310Sstevel@tonic-gate { 2320Sstevel@tonic-gate return buffer->buf + buffer->offset; 2330Sstevel@tonic-gate } 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate /* Dumps the contents of the buffer to stderr. */ 2360Sstevel@tonic-gate void 2370Sstevel@tonic-gate buffer_dump(Buffer *buffer) 2380Sstevel@tonic-gate { 2392757Sjp161948 u_int i; 2400Sstevel@tonic-gate u_char *ucp = buffer->buf; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate for (i = buffer->offset; i < buffer->end; i++) { 2430Sstevel@tonic-gate fprintf(stderr, "%02x", ucp[i]); 2440Sstevel@tonic-gate if ((i-buffer->offset)%16==15) 245*7574SJan.Pechanec@Sun.COM fprintf(stderr, "\n"); 2460Sstevel@tonic-gate else if ((i-buffer->offset)%2==1) 2470Sstevel@tonic-gate fprintf(stderr, " "); 2480Sstevel@tonic-gate } 249*7574SJan.Pechanec@Sun.COM 250*7574SJan.Pechanec@Sun.COM if (buffer->offset == buffer->end) { 251*7574SJan.Pechanec@Sun.COM /* explicitly state when the buffer is empty */ 252*7574SJan.Pechanec@Sun.COM fprintf(stderr, "<EMPTY BUFFER>\n"); 253*7574SJan.Pechanec@Sun.COM } else { 254*7574SJan.Pechanec@Sun.COM /* print the terminal '\n' if it wasn't already printed */ 255*7574SJan.Pechanec@Sun.COM if ((i - buffer->offset) % 16 != 0) 256*7574SJan.Pechanec@Sun.COM fprintf(stderr, "\n"); 257*7574SJan.Pechanec@Sun.COM } 258*7574SJan.Pechanec@Sun.COM /* 259*7574SJan.Pechanec@Sun.COM * We want an extra empty line after the packet dump for better 260*7574SJan.Pechanec@Sun.COM * readability. 261*7574SJan.Pechanec@Sun.COM */ 262*7574SJan.Pechanec@Sun.COM fprintf(stderr, "\n"); 2630Sstevel@tonic-gate } 264