/* $Id: ssh_buffer.c,v 1.55 2001/02/11 03:35:10 tls Exp $ */ /* * Copyright 1999 RedBack Networks, Incorporated. * All rights reserved. * * This software is not in the public domain. It is distributed * under the terms of the license in the file LICENSE in the * same directory as this file. If you have received a copy of this * software without the LICENSE file (which means that whoever gave * you this software violated its license) you may obtain a copy from * http://www.panix.com/~tls/LICENSE.txt */ /* * Copyright (c) 2000 Andrew Brown and Eric Haszlakiewicz. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Various functions to deal with buffers. */ #include #include #include #include #include #include #include #include #include #include #include "options.h" #include "ssh_buffer.h" #include "ssh_logging.h" #include "ssh_rsakeys.h" #include "ssh_util.h" /* Buffer management functions: */ /* * Allocate a buffer. (re)Initialize it. */ struct ssh_buf * buf_alloc(struct ssh_buf * buf, int size) { struct ssh_buf *tmpbuf; u_int8_t *ndata; if (size < 0) { SSH_DLOG(3, ("bad size: %d\n", size)); errno = EINVAL; return (NULL); } /* Allocate the actual structure, if necessary. */ if (!buf) buf = tmpbuf = calloc(1, sizeof(struct ssh_buf)); else tmpbuf = NULL; if (!buf) { SSH_DLOG(2, ("malloc of ssh_buf failed: %s\n", strerror(errno))); errno = ENOMEM; return (NULL); } /* If we've already got enough, just return. */ if (buf->dsize >= size) { buf_reset(buf); return (buf); } /* Fill in the fields of the buffer. */ if (buf->data) ndata = realloc(buf->data, size); else ndata = malloc(size); if (ndata == NULL) { SSH_DLOG(2, ("alloc of buffer failed: %s\n", strerror(errno))); if (tmpbuf); free(tmpbuf); /* if we allocated a ssh_buf struct above */ return (NULL); } buf->data = ndata; buf->dsize = size; buf_reset(buf); return (buf); } /* * Grow a buffer to a specified size. */ int buf_grow(struct ssh_buf * buf, int size) { u_int8_t *odata; if (size < buf->dsize) return (0); odata = buf->data; buf->data = realloc(buf->data, size); if (buf->data == NULL) { SSH_DLOG(2, ("realloc failed: %s\n", strerror(errno))); buf->data = odata; errno = ENOSPC; return (-1); } buf->dsize = size; /* Readjust pointers if the start of data moved. */ if (buf->data != odata) buf->get_loc = buf->data + (buf->get_loc - odata); return (0); } /* * Make a certain amount of space available for use. * * Rounds up to next highest 1024 bytes. */ int buf_makeavail(struct ssh_buf * buf, int space) { int newsize; /* First try trimming used space */ buf_trim(buf, -1); if (buf_avail(buf) < space) { /* Guess we have to try allocating more memory */ newsize = buf->dlen + (space - buf_avail(buf)); newsize = roundup(newsize, 1024); if (buf_grow(buf, newsize) != 0) { return (1); } } return (0); } /* * Zero out space associated with a buffer and reset pointers. */ void buf_clear(struct ssh_buf * buf) { if (!buf) return; if (buf->data) memset(buf->data, 0, buf->dsize); buf_reset(buf); } /* * Destroy a buffer and cleanup any space associated with it. * * Note: this _does_not_free_ the actual ssh_buf structure. */ void buf_cleanup(struct ssh_buf * buf) { buf_clear(buf); if (buf->data) free(buf->data); memset(buf, 0, sizeof(struct ssh_buf)); } /* * Append the contents of one buffer to the other. * The source buffer is unmodified. * * The destination buffer is expanded as necessary. */ int buf_append(struct ssh_buf * dstbuf, const struct ssh_buf * srcbuf) { if (buffer_expand(dstbuf, buf_alllen(dstbuf) + buf_alllen(srcbuf), "buf_append") != 0) return (1); memcpy(buf_endofdata(dstbuf), buf_alldata(srcbuf), buf_alllen(srcbuf)); dstbuf->dlen += buf_alllen(srcbuf); return (0); } /* * Adjust the length of data in the buffer. * * Use this to fix the length of the data after * writing directly into the buffer. * * Note: this could cause get_loc > data+dlen, but that's ok. */ int buf_adjlen(struct ssh_buf * buf, int delta) { if ((buf->dlen + delta) < 0 || (buf->dlen + delta) > buf->dsize) { SSH_DLOG(3, ("len w/delta out of range: %d+%d:%d\n", buf->dlen, delta, buf->dsize)); errno = EINVAL; return (1); } buf->dlen += delta; return (0); } /* * Trim bytes from the beginning of the buffer. * Copy any extra data to the beginning. * * If nbytes==-1 this functions trims any used up data. */ int buf_trim(struct ssh_buf * buf, int nbytes) { if (buf->dlen < nbytes) { SSH_DLOG(3, ("nbytes to trim > length of data.\n")); errno = EINVAL; return (1); } /* Auto trim used up data: */ if (nbytes < 0) nbytes = buf->get_loc - buf->data; if (nbytes > 0) { memcpy(buf->data, buf->data + nbytes, buf->dlen - nbytes); buf->dlen -= nbytes; } if ((buf->get_loc - buf->data) < nbytes) buf->get_loc = buf->data; /* sketchy... */ else buf->get_loc -= nbytes; return (0); } /* * Don't get anything, just skip the specified number of bytes. */ int buf_get_skip(struct ssh_buf * buf, int nbytes) { if (!buf->data || ((buf->data + buf->dlen) < (buf->get_loc + nbytes)) || ((buf->data) > (buf->get_loc + nbytes))) { SSH_DLOG(3, ("out of data, skipping %d bytes\n", nbytes)); errno = EAGAIN; return (1); } buf->get_loc += nbytes; return (0); } int buf_get_int32(struct ssh_buf * buf, u_int32_t * ret) { u_int32_t tmp; if (!buf->data || ((buf->data + buf->dlen) < (buf->get_loc + sizeof(u_int32_t)))) { SSH_DLOG(3, ("out of data, u_int32_t\n")); errno = EAGAIN; return (1); } memcpy(&tmp, buf->get_loc, sizeof(u_int32_t)); *ret = ntohl(tmp); buf->get_loc += sizeof(u_int32_t); return (0); } int buf_get_int16(struct ssh_buf * buf, u_int16_t * ret) { u_int16_t tmp; if (!buf->data || ((buf->data + buf->dlen) < (buf->get_loc + sizeof(u_int16_t)))) { SSH_DLOG(3, ("out of data, u_int16_t\n")); errno = EAGAIN; return (1); } memcpy(&tmp, buf->get_loc, sizeof(u_int16_t)); *ret = ntohs(tmp); buf->get_loc += sizeof(u_int16_t); return (0); } int buf_get_int8(struct ssh_buf * buf, u_int8_t * ret) { if (!buf->data || ((buf->data + buf->dlen) < (buf->get_loc + sizeof(u_int8_t)))) { SSH_DLOG(3, ("out of data, u_int8_t\n")); errno = EAGAIN; return (1); } *ret = *(u_int8_t *) (buf->get_loc); buf->get_loc += sizeof(u_int8_t); return (0); } /* * buf_get_nbytes: Get a number of bytes. * val is allocated to more than nbytes to allow it to be * null terminated later. */ int buf_get_nbytes(struct ssh_buf * buf, int nbytes, u_int8_t ** val) { if ((buf->get_loc + nbytes) > (buf->data + buf->dlen)) { /* out of data. */ SSH_DLOG(3, ("out of data, getting %d bytes\n", nbytes)); return (1); } if (*val != NULL) { SSH_DLOG(3, ("*val is not null, %p\n", *val)); return (1); } if ((*val = malloc(nbytes + 2)) == NULL) { SSH_ERROR("Unable to allocate mem: %s\n", strerror(errno)); return (1); } memcpy(*val, (u_int8_t *) (buf->get_loc), nbytes); buf->get_loc += nbytes; return (0); } /* * Get an unknown number of bytes from the buffer. * (i.e. all of them.) * * Note: caller must free *val. */ int buf_get_unk_bytes(struct ssh_buf * buf, u_int8_t ** val, int *len) { if (*val != NULL) { SSH_DLOG(3, ("*val is not null, %p\n", *val)); return (1); } if (buf->get_loc < (buf->data + buf->dlen)) { *len = ((buf->data + buf->dlen) - buf->get_loc); /* Length + 2, in case caller wants to null-terminate. */ if ((*val = malloc(*len + 2)) == NULL) { SSH_ERROR("Unable to allocate mem: %s\n", strerror(errno)); *len = 0; return (1); } memcpy(*val, (u_int8_t *) (buf->get_loc), *len); buf->get_loc = buf->data + buf->dlen; } else { *len = 0; *val = NULL; } return (0); } /* * Get a ssh style binary string from the buffer. * * If *vals is NULL a suitable chunk is allocated. * * If len is NULL the string is NULL terminated. */ int buf_get_binstr(struct ssh_buf * buf, u_int8_t ** vals, u_int32_t * len) { u_int32_t tmpint; if (!len) len = &tmpint; if (buf_get_int32(buf, len) != 0) { SSH_DLOG(3, ("out of data, binstr\n")); errno = EAGAIN; return (1); } if (buf_len(buf) < *len) { SSH_DLOG(3, ("binstr len(%d) > buf_len(%d)", *len, buf_len(buf))); errno = EAGAIN; return (1); } if (!(*vals)) { /* len + 2 in case caller want to 0 terminate. */ if (((*vals) = malloc(*len + 2)) == NULL) return (1); } memcpy(*vals, buf->get_loc, *len); buf->get_loc += *len; if (len == &tmpint) (*vals)[*len] = '\0'; /* Null terminate */ return (0); } /* * Return a ssh style binary string from the buffer. No allocation is * done and the string is *not* copied, which should save time. The * string is *not guaranteed* to be null terminated either (it may * even contain nulls), so the length should always be used for * write(2) or *printf(3). The part of the buffer that the string * comes from *is* marked as used up. Use these strings carefully, * don't attempt to deallocate them, and don't pass them back to * calling functions without coping them. (similar to inet_ntoa(3)). */ int buf_use_binstr(struct ssh_buf * buf, u_int8_t ** vals, u_int32_t * len) { if (!len || !vals || *vals) { SSH_DLOG(3, ("illegal usage of use_binstr")); return 1; } if (buf_get_int32(buf, len) != 0) { SSH_DLOG(3, ("out of data, binstr\n")); errno = EAGAIN; return (1); } if (buf_len(buf) < *len) { SSH_DLOG(3, ("binstr len(%d) > buf_len(%d)", *len, buf_len(buf))); errno = EAGAIN; return (1); } *vals = buf->get_loc; buf->get_loc += *len; return (0); } /* * Get a zero terminated string from the buffer. */ int buf_get_asciiz(struct ssh_buf * buf, char **astring, int *len) { int slen; u_int8_t *zero; if (buf->get_loc >= (buf->data + buf->dlen)) { SSH_DLOG(3, ("out of data.\n")); errno = EAGAIN; return (1); } if ((zero = memchr(buf->get_loc, 0, buf_len(buf))) == NULL) slen = buf_len(buf); else slen = strlen(buf->get_loc); if ((*astring) != NULL) { SSH_DLOG(3, ("*astring is not null, %p", *astring)); return (1); } else if (((*astring) = malloc((slen + 1) * sizeof(char))) == NULL) { errno = EAGAIN; return (1); } memcpy(*astring, buf->get_loc, slen); (*astring)[slen] = 0; if (len) *len = slen; buf->get_loc += slen; if (zero) buf->get_loc++; /* Skip the newline too */ return (0); } /* * Get a newline terminated string from the buffer. * Note: this ignores \0's within the line. * * The string is returned without the newline. */ int buf_get_line(struct ssh_buf * buf, char **astring, int *len) { int slen; u_int8_t *newline; if (buf->get_loc >= (buf->data + buf->dlen)) { SSH_DLOG(3, ("out of data.\n")); errno = EAGAIN; return (1); } if ((newline = memchr(buf->get_loc, '\n', buf_len(buf))) == NULL) slen = buf_len(buf); else slen = (newline - (buf->get_loc)); /* data w/o newline */ if ((*astring) != NULL) { SSH_DLOG(3, ("*astring is not null, %p", *astring)); return (1); } /* Allocate room for string + terminating NULL */ else if (((*astring) = malloc((slen + 1) * sizeof(char))) == NULL) { errno = EAGAIN; return (1); } memcpy(*astring, buf->get_loc, slen); (*astring)[slen] = '\0'; if (len) *len = slen; buf->get_loc += slen; if (newline) buf->get_loc++; /* Skip the newline too */ return (0); } int buf_get_bignum(struct ssh_buf * buf, ssh_BIGNUM ** num) { u_int16_t bits, bytes; if (buf_get_int16(buf, &bits) != 0) return (1); bytes = (bits + 7) / 8; if ((bytes > buf_len(buf)) || (bignum_bin2bn(num, buf->get_loc, bytes) != 0)) { buf->get_loc -= sizeof(u_int16_t); return (1); } buf->get_loc += bytes; return (0); } int buf_get_mpint(struct ssh_buf * buf, struct ssh_mpint * val) { u_int16_t bits, bytes; if (buf_get_int16(buf, &bits) != 0) return (1); bytes = (bits + 7) / 8; if (buf_get_nbytes(buf, bytes, &(val->data)) != 0) { buf->get_loc -= sizeof(u_int16_t); return (1); } val->bits = bits; return (0); } int buffer_expand(struct ssh_buf * buf, size_t size, const char *funcname) { u_int8_t *newbuf; int newsize; while ((buf->dlen + size) > buf->dsize) { newsize = ((buf->dsize / 1024) + 1) * 1024; newbuf = realloc(buf->data, newsize); if (newbuf == NULL) { SSH_ERROR("%s: realloc to %d failed: %s\n", funcname, newsize, strerror(errno)); return (1); } buf->data = newbuf; buf->dsize = newsize; } return (0); } /* * Add a single byte to the end of the buffer. */ int buf_put_byte(struct ssh_buf * buf, u_int8_t val) { if (buffer_expand(buf, sizeof(u_int8_t), "buf_put_byte") != 0) return (1); *(buf->data + buf->dlen) = val; buf->dlen += sizeof(u_int8_t); return (0); } /* * Put a number of bytes to the end of the buffer. */ int buf_put_nbytes(struct ssh_buf * buf, int nbytes, const u_int8_t * vals) { if (buffer_expand(buf, nbytes * sizeof(u_int8_t), "buf_put_nbytes") != 0) return (1); memcpy(buf->data + buf->dlen, vals, nbytes * sizeof(u_int8_t)); buf->dlen += nbytes * sizeof(u_int8_t); return (0); } /* * Put a 32-bit integer to the end of the buffer. */ int buf_put_int32(struct ssh_buf * buf, u_int32_t val) { u_int32_t tmp; if (buffer_expand(buf, sizeof(u_int32_t), "buf_put_int32") != 0) return (1); tmp = htonl(val); memcpy(buf->data + buf->dlen, &tmp, sizeof(u_int32_t)); buf->dlen += sizeof(u_int32_t); return (0); } /* * Put a 16-bit integer to the end of the buffer. */ int buf_put_int16(struct ssh_buf * buf, u_int16_t val) { u_int16_t tmp; if (buffer_expand(buf, sizeof(u_int16_t), "buf_put_int16") != 0) return (1); tmp = htons(val); memcpy(buf->data + buf->dlen, &tmp, sizeof(u_int16_t)); buf->dlen += sizeof(u_int16_t); return (0); } /* * Put a ssh style binary string to the end of the buffer. */ int buf_put_binstr(struct ssh_buf * buf, const u_int8_t * vals, u_int32_t len) { if (buffer_expand(buf, (len + sizeof(u_int32_t)) * sizeof(u_int8_t), "buf_put_binstr") != 0) return (1); setbinstr_len(buf->data + buf->dlen, len); memcpy(buf->data + buf->dlen + sizeof(u_int32_t), vals, len); buf->dlen += len + sizeof(u_int32_t); return (0); } /* * Put a zero terminated ascii scring. */ int buf_put_asciiz(struct ssh_buf * buf, const char *astring) { int slen; slen = strlen(astring) + 1; if (buffer_expand(buf, slen * sizeof(char), "buf_put_asciiz") != 0) return (1); strncpy(buf->data + buf->dlen, astring, slen); buf->dlen += slen; return (0); } /* * Put a mpint to the end of the buffer. */ int buf_put_mpint(struct ssh_buf * buf, struct ssh_mpint * val) { int len; len = (val->bits + 7) / 8; if (buf_put_int16(buf, val->bits) != 0) return (1); /* XXX if this fails, remove the int16? */ return (buf_put_nbytes(buf, len, val->data)); } /* * Put a BIGNUM. Looks just like an mpint. */ int buf_put_bignum(struct ssh_buf * buf, const ssh_BIGNUM * num) { if (buf_put_int16(buf, bignum_num_bits(num)) != 0) return (1); if (buffer_expand(buf, bignum_num_bytes(num), "buf_put_bignum") != 0) { /* XXX trim the int16? */ return (1); } buf->dlen += bignum_bn2bin(num, buf->data + buf->dlen); return (0); } /* * Put/Get a RSA public key with each component as a mpint to the end * of the buffer. */ int buf_put_rsa_publickey(struct ssh_buf * buf, const ssh_RSA * key) { struct ssh_mpint mpi; if (buf_put_int32(buf, bignum_num_bits(ssh_rsa_npart(key))) != 0) { SSH_DLOG(2, ("unable to put num bits.\n")); return (1); } if (bignum_to_mpint(ssh_rsa_epart(key), &mpi) < 0) { SSH_DLOG(2, ("unable to convert exponent to mpint\n")); return (1); } if (buf_put_mpint(buf, &mpi) != 0) { SSH_DLOG(2, ("unable to put exponent.\n")); free(mpi.data); mpi.data = NULL; return (1); } free(mpi.data); mpi.data = NULL; if (bignum_to_mpint(ssh_rsa_npart(key), &mpi) < 0) { SSH_DLOG(2, ("unable to convert modulus to mpint\n")); return (1); } if (buf_put_mpint(buf, &mpi) != 0) { SSH_DLOG(2, ("unable to put modulus.\n")); free(mpi.data); mpi.data = NULL; return (1); } free(mpi.data); mpi.data = NULL; return (0); } int buf_get_rsa_publickey(struct ssh_buf * buf, ssh_RSA * key, u_int32_t * len) { struct ssh_mpint mpi; memset(&mpi, 0, sizeof(mpi)); if (!len || buf_get_int32(buf, len) != 0) { SSH_DLOG(2, ("unable to get num bits.\n")); return (1); } if (buf_get_mpint(buf, &mpi) != 0) { SSH_DLOG(2, ("unable to get exponent.\n")); free(mpi.data); mpi.data = NULL; return (1); } if (mpint_to_bignum(&mpi, &ssh_rsa_epart(key)) < 0) { SSH_DLOG(2, ("unable to convert mpint to exponent\n")); free(mpi.data); mpi.data = NULL; return (1); } free(mpi.data); mpi.data = NULL; if (buf_get_mpint(buf, &mpi) != 0) { SSH_DLOG(2, ("unable to put modulus.\n")); free(mpi.data); mpi.data = NULL; return (1); } if (mpint_to_bignum(&mpi, &ssh_rsa_npart(key)) < 0) { SSH_DLOG(2, ("unable to convert mpint to modulus\n")); free(mpi.data); mpi.data = NULL; return (1); } free(mpi.data); mpi.data = NULL; return (0); } /* * Reads from d into buf. Returns total length of buf. */ int buf_readfile(struct ssh_buf * buf, int d, int size) { int ret; if (!buf) { errno = EINVAL; return (-1); } if (size < 0) size = buf_avail(buf); if (size > buf_avail(buf)) { if (buf_grow(buf, size + buf_alllen(buf)) != 0) { SSH_ERROR("buf_grow failed: %s\n", strerror(errno)); return (-1); } } if ((ret = read(d, buf_endofdata(buf), size)) < 0) { if (errno == EINTR) errno = EAGAIN; if (errno != EAGAIN) SSH_ERROR("read failed: %s\n", strerror(errno)); return (-1); } if (ret == 0) return (0); buf->dlen += ret; return (buf->dlen); } /* * Fills a buffer with data from d. * It will try to read as much data as there is room for. * * If that fails then it will try a blocking read to get at least * bytes read. * * If either read it interrupted by a signal we return. * * Returns: * -1 on error * 0 on EOF * amount of data read */ int buf_fillbuf(ssize_t(*xread) (int, void *, size_t), struct ssh_buf * buf, int d, int size) { int total_read; int ret; int tmp_err; int old_flag; assert(buf != NULL); /* * Negative size means we want to read as much as possible. * We always read as much as possible, so set the size wanted to 1. */ if (size < 0) size = 1; if (size > buf_avail(buf)) { if (buf_grow(buf, size + buf_alllen(buf)) != 0) { tmp_err = errno; SSH_ERROR("buf_grow failed: %s\n", strerror(errno)); errno = tmp_err; return (-1); } } if (buf_avail(buf) == 0) { SSH_ERROR("No room available in the buffer!"); return (-1); } if ((ret = xread(d, buf_endofdata(buf), buf_avail(buf))) < 0) { tmp_err = errno; if (errno == EINTR) { SSH_DLOG(4, ("read(#1) got interrupted\n")); errno = tmp_err; return (-1); } if (errno != EAGAIN) { SSH_ERROR("read failed: %s\n", strerror(errno)); errno = tmp_err; return (-1); } /* errno == EAGAIN, no data yet, so try blocking read below */ total_read = 0; } else { buf->dlen += ret; /* Check for EOF */ if (ret == 0) return (0); total_read = ret; } if (size <= total_read) return (total_read); size -= total_read; /* If we haven't read enough try a blocking read. */ old_flag = fcntl(d, F_GETFL); fcntl(d, F_SETFL, old_flag & ~O_NONBLOCK); while (size > 0) { if ((ret = xread(d, buf_endofdata(buf), size)) < 0) { tmp_err = errno; /* Restore previous (non-)blocking behaviour */ fcntl(d, F_SETFL, old_flag); if (tmp_err == EINTR) { SSH_DLOG(4, ("read(#2) got interrupted\n")); errno = EINTR; if (total_read == 0) return (-1); /* same as read() */ return (total_read); } /* Note: on errors we might still have read something */ SSH_ERROR("read failed: %s\n", strerror(errno)); errno = tmp_err; return (-1); } if (ret == 0) return (total_read); total_read += ret; buf->dlen += ret; size -= ret; } /* Restore previous blocking behaviour */ fcntl(d, F_SETFL, old_flag); return (total_read); } void hex_dump(const unsigned char *buf, int len) { int i, j; char line[80], tmp[12]; memset(line, ' ', sizeof(line)); for (i = 0, j = 15; i < len; i++) { j = i % 16; /* reset line */ if (j == 0) { line[58] = '|'; line[77] = '|'; line[78] = 0; snprintf(tmp, sizeof(tmp), "%07d", i); memcpy(&line[0], tmp, 7); } /* copy out hex version of byte */ snprintf(tmp, sizeof(tmp), "%02x", buf[i]); memcpy(&line[9 + j * 3], tmp, 2); /* copy out plain version of byte */ line[60 + j] = (isprint(buf[i])) ? buf[i] : '.'; /* print a full line and erase it */ if (j == 15) { fprintf(stderr, "%s\r\n", line); memset(line, ' ', sizeof(line)); } } if (line[0] != ' ') fprintf(stderr, "%s\r\n", line); fprintf(stderr, "%07d bytes\r\n", len); } /* * Print out the contents of a buffer. */ void buf_printnx(const struct ssh_buf * buf, int nbytes, int printall) { int len; u_int8_t *data; len = printall ? buf_alllen(buf) : buf_len(buf); data = printall ? buf_alldata(buf) : buf_data(buf); if (nbytes > len) nbytes = len; if (len == 0) { fprintf(stderr, "(empty)\r\n"); return; } hex_dump(data, nbytes); }