version 1.35, 2014/02/02 03:44:31 |
version 1.36, 2014/04/30 05:29:56 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
|
|
/* |
/* |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Copyright (c) 2012 Damien Miller <djm@mindrot.org> |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
|
* All rights reserved |
|
* Functions for manipulating fifo buffers (that can grow if needed). |
|
* |
* |
* As far as I am concerned, the code I have written for this software |
* Permission to use, copy, modify, and distribute this software for any |
* can be used freely for any purpose. Any derived versions of this |
* purpose with or without fee is hereby granted, provided that the above |
* software must be clearly marked as such, and if the derived work is |
* copyright notice and this permission notice appear in all copies. |
* incompatible with the protocol description in the RFC file, it must be |
* |
* called by a name other than "ssh" or "Secure Shell". |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
*/ |
*/ |
|
|
#include <sys/param.h> |
/* Emulation wrappers for legacy OpenSSH buffer API atop sshbuf */ |
|
|
#include <stdio.h> |
#include <sys/types.h> |
#include <string.h> |
|
#include <stdarg.h> |
|
#include <stdlib.h> |
|
|
|
#include "xmalloc.h" |
|
#include "buffer.h" |
#include "buffer.h" |
#include "log.h" |
#include "log.h" |
|
#include "ssherr.h" |
|
|
#define BUFFER_MAX_CHUNK 0x100000 |
|
#define BUFFER_MAX_LEN 0xa00000 |
|
#define BUFFER_ALLOCSZ 0x008000 |
|
|
|
/* Initializes the buffer structure. */ |
|
|
|
void |
void |
buffer_init(Buffer *buffer) |
|
{ |
|
const u_int len = 4096; |
|
|
|
buffer->alloc = 0; |
|
buffer->buf = xmalloc(len); |
|
buffer->alloc = len; |
|
buffer->offset = 0; |
|
buffer->end = 0; |
|
} |
|
|
|
/* Frees any memory used for the buffer. */ |
|
|
|
void |
|
buffer_free(Buffer *buffer) |
|
{ |
|
if (buffer->alloc > 0) { |
|
explicit_bzero(buffer->buf, buffer->alloc); |
|
buffer->alloc = 0; |
|
free(buffer->buf); |
|
} |
|
} |
|
|
|
/* |
|
* Clears any data from the buffer, making it empty. This does not actually |
|
* zero the memory. |
|
*/ |
|
|
|
void |
|
buffer_clear(Buffer *buffer) |
|
{ |
|
buffer->offset = 0; |
|
buffer->end = 0; |
|
} |
|
|
|
/* Appends data to the buffer, expanding it if necessary. */ |
|
|
|
void |
|
buffer_append(Buffer *buffer, const void *data, u_int len) |
buffer_append(Buffer *buffer, const void *data, u_int len) |
{ |
{ |
void *p; |
int ret; |
p = buffer_append_space(buffer, len); |
|
memcpy(p, data, len); |
|
} |
|
|
|
static int |
if ((ret = sshbuf_put(buffer, data, len)) != 0) |
buffer_compact(Buffer *buffer) |
fatal("%s: %s", __func__, ssh_err(ret)); |
{ |
|
/* |
|
* If the buffer is quite empty, but all data is at the end, move the |
|
* data to the beginning. |
|
*/ |
|
if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { |
|
memmove(buffer->buf, buffer->buf + buffer->offset, |
|
buffer->end - buffer->offset); |
|
buffer->end -= buffer->offset; |
|
buffer->offset = 0; |
|
return (1); |
|
} |
|
return (0); |
|
} |
} |
|
|
/* |
|
* Appends space to the buffer, expanding the buffer if necessary. This does |
|
* not actually copy the data into the buffer, but instead returns a pointer |
|
* to the allocated region. |
|
*/ |
|
|
|
void * |
void * |
buffer_append_space(Buffer *buffer, u_int len) |
buffer_append_space(Buffer *buffer, u_int len) |
{ |
{ |
u_int newlen; |
int ret; |
void *p; |
u_char *p; |
|
|
if (len > BUFFER_MAX_CHUNK) |
if ((ret = sshbuf_reserve(buffer, len, &p)) != 0) |
fatal("buffer_append_space: len %u not supported", len); |
fatal("%s: %s", __func__, ssh_err(ret)); |
|
return p; |
/* If the buffer is empty, start using it from the beginning. */ |
|
if (buffer->offset == buffer->end) { |
|
buffer->offset = 0; |
|
buffer->end = 0; |
|
} |
|
restart: |
|
/* If there is enough space to store all data, store it now. */ |
|
if (buffer->end + len < buffer->alloc) { |
|
p = buffer->buf + buffer->end; |
|
buffer->end += len; |
|
return p; |
|
} |
|
|
|
/* Compact data back to the start of the buffer if necessary */ |
|
if (buffer_compact(buffer)) |
|
goto restart; |
|
|
|
/* Increase the size of the buffer and retry. */ |
|
newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); |
|
if (newlen > BUFFER_MAX_LEN) |
|
fatal("buffer_append_space: alloc %u not supported", |
|
newlen); |
|
buffer->buf = xrealloc(buffer->buf, 1, newlen); |
|
buffer->alloc = newlen; |
|
goto restart; |
|
/* NOTREACHED */ |
|
} |
} |
|
|
/* |
|
* Check whether an allocation of 'len' will fit in the buffer |
|
* This must follow the same math as buffer_append_space |
|
*/ |
|
int |
int |
buffer_check_alloc(Buffer *buffer, u_int len) |
buffer_check_alloc(Buffer *buffer, u_int len) |
{ |
{ |
if (buffer->offset == buffer->end) { |
int ret = sshbuf_check_reserve(buffer, len); |
buffer->offset = 0; |
|
buffer->end = 0; |
|
} |
|
restart: |
|
if (buffer->end + len < buffer->alloc) |
|
return (1); |
|
if (buffer_compact(buffer)) |
|
goto restart; |
|
if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) |
|
return (1); |
|
return (0); |
|
} |
|
|
|
/* Returns the number of bytes of data in the buffer. */ |
if (ret == 0) |
|
return 1; |
u_int |
if (ret == SSH_ERR_NO_BUFFER_SPACE) |
buffer_len(const Buffer *buffer) |
return 0; |
{ |
fatal("%s: %s", __func__, ssh_err(ret)); |
return buffer->end - buffer->offset; |
|
} |
} |
|
|
/* Gets data from the beginning of the buffer. */ |
|
|
|
int |
int |
buffer_get_ret(Buffer *buffer, void *buf, u_int len) |
buffer_get_ret(Buffer *buffer, void *buf, u_int len) |
{ |
{ |
if (len > buffer->end - buffer->offset) { |
int ret; |
error("buffer_get_ret: trying to get more bytes %d than in buffer %d", |
|
len, buffer->end - buffer->offset); |
if ((ret = sshbuf_get(buffer, buf, len)) != 0) { |
return (-1); |
error("%s: %s", __func__, ssh_err(ret)); |
|
return -1; |
} |
} |
memcpy(buf, buffer->buf + buffer->offset, len); |
return 0; |
buffer->offset += len; |
|
return (0); |
|
} |
} |
|
|
void |
void |
buffer_get(Buffer *buffer, void *buf, u_int len) |
buffer_get(Buffer *buffer, void *buf, u_int len) |
{ |
{ |
if (buffer_get_ret(buffer, buf, len) == -1) |
if (buffer_get_ret(buffer, buf, len) == -1) |
fatal("buffer_get: buffer error"); |
fatal("%s: buffer error", __func__); |
} |
} |
|
|
/* Consumes the given number of bytes from the beginning of the buffer. */ |
|
|
|
int |
int |
buffer_consume_ret(Buffer *buffer, u_int bytes) |
buffer_consume_ret(Buffer *buffer, u_int bytes) |
{ |
{ |
if (bytes > buffer->end - buffer->offset) { |
int ret = sshbuf_consume(buffer, bytes); |
error("buffer_consume_ret: trying to get more bytes than in buffer"); |
|
return (-1); |
if (ret == 0) |
} |
return 0; |
buffer->offset += bytes; |
if (ret == SSH_ERR_MESSAGE_INCOMPLETE) |
return (0); |
return -1; |
|
fatal("%s: %s", __func__, ssh_err(ret)); |
} |
} |
|
|
void |
void |
buffer_consume(Buffer *buffer, u_int bytes) |
buffer_consume(Buffer *buffer, u_int bytes) |
{ |
{ |
if (buffer_consume_ret(buffer, bytes) == -1) |
if (buffer_consume_ret(buffer, bytes) == -1) |
fatal("buffer_consume: buffer error"); |
fatal("%s: buffer error", __func__); |
} |
} |
|
|
/* Consumes the given number of bytes from the end of the buffer. */ |
|
|
|
int |
int |
buffer_consume_end_ret(Buffer *buffer, u_int bytes) |
buffer_consume_end_ret(Buffer *buffer, u_int bytes) |
{ |
{ |
if (bytes > buffer->end - buffer->offset) |
int ret = sshbuf_consume_end(buffer, bytes); |
return (-1); |
|
buffer->end -= bytes; |
if (ret == 0) |
return (0); |
return 0; |
|
if (ret == SSH_ERR_MESSAGE_INCOMPLETE) |
|
return -1; |
|
fatal("%s: %s", __func__, ssh_err(ret)); |
} |
} |
|
|
void |
void |
buffer_consume_end(Buffer *buffer, u_int bytes) |
buffer_consume_end(Buffer *buffer, u_int bytes) |
{ |
{ |
if (buffer_consume_end_ret(buffer, bytes) == -1) |
if (buffer_consume_end_ret(buffer, bytes) == -1) |
fatal("buffer_consume_end: trying to get more bytes than in buffer"); |
fatal("%s: buffer error", __func__); |
} |
} |
|
|
/* Returns a pointer to the first used byte in the buffer. */ |
|
|
|
void * |
|
buffer_ptr(const Buffer *buffer) |
|
{ |
|
return buffer->buf + buffer->offset; |
|
} |
|
|
|
/* Dumps the contents of the buffer to stderr. */ |
|
|
|
void |
|
buffer_dump(const Buffer *buffer) |
|
{ |
|
u_int i; |
|
u_char *ucp = buffer->buf; |
|
|
|
for (i = buffer->offset; i < buffer->end; i++) { |
|
fprintf(stderr, "%02x", ucp[i]); |
|
if ((i-buffer->offset)%16==15) |
|
fprintf(stderr, "\r\n"); |
|
else if ((i-buffer->offset)%2==1) |
|
fprintf(stderr, " "); |
|
} |
|
fprintf(stderr, "\r\n"); |
|
} |
|