/* $CoreSDI: buffer.c,v 1.12 2001/08/30 21:33:13 claudio Exp $ */ /* * Copyright (c) 2000, 2001, Core SDI S.A., Argentina * 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. Neither name of the Core SDI S.A. nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /* * Buffer - Generic buffer functions * Author: Claudio Castiglia */ #include #include #ifndef WIN32 #include #else #include #endif /* WIN32 */ #ifdef __linux__ #include #elif !defined(WIN32) #include #endif /* __linux__ */ #include #include #include #include #ifndef WIN32 #include #endif #include #include #include "sysdep.h" #include "buffer.h" /* * Buffer structure: * rpos wpos size * +----------+--------------+-----------------------------+ * |used data | util data | free space | * +----------+--------------+-----------------------------+ * inutil data util space */ /* * _buf_get_free_space(): */ static int32_t _buf_get_free_space(BUFFER *buf) { return (buf->buf_size - buf->buf_wpos); } /* * _buf_get_util_space(): */ static int32_t _buf_get_util_space(BUFFER *buf) { return (buf->buf_wpos - buf->buf_rpos); } /* * _buf_put_raw(): */ static void _buf_put_raw(BUFFER *buf, const void *src, int32_t srclen) { memcpy(buf->buf_data + buf->buf_wpos, src, srclen); buf->buf_wpos += srclen; } /* * _buf_get_raw(): */ static void _buf_get_raw(BUFFER *buf, void *dst, int32_t dstlen) { memcpy(dst, buf->buf_data + buf->buf_rpos, dstlen); buf->buf_rpos += dstlen; } /* * buf_create(): * Create a bufsiz bytes buffer and return a pointer to it, * on error return NULL and set errno. */ BUFFER * buf_create(int32_t bufsiz) { BUFFER *buf; if (bufsiz <= 0) errno = EINVAL; else { buf = (BUFFER *) malloc(sizeof(BUFFER)); if (buf != NULL) { buf->buf_data = (u_int8_t *) malloc(sizeof(u_int8_t) * bufsiz); if (buf->buf_data != NULL) { buf->buf_size = bufsiz; buf->buf_rpos = 0; buf->buf_wpos = 0; return (buf); } free(buf); } } return (NULL); } /* * buf_release(): * Release a buffer. */ void buf_release(BUFFER *buf, int flag) { if (buf != NULL) { if (flag == BUFFER_CLEAN) bzero(buf->buf_data, buf->buf_size); free(buf->buf_data); free(buf); } } /* * buf_reset(): * Reset buffer (mark it as unused). */ void buf_reset(BUFFER *buf) { if (buf != NULL) { buf->buf_wpos = 0; buf->buf_rpos = 0; } } /* * buf_available(): * Return no. of free bytes on buffer, and -1 on error. */ int32_t buf_available(BUFFER *buf) { if (buf == NULL) { errno = EINVAL; return (-1); } return (_buf_get_free_space(buf)); } /* * buf_isempty(): * Return a positive value if buffer is empty, 0 if not, * and -1 on error. */ int buf_isempty(BUFFER *buf) { if (buf == NULL) { errno = EINVAL; return (-1); } return (_buf_get_free_space(buf) == buf->buf_size); } /* * buf_isfull(): * Return a positive value if buffer is full, 0 if not, * and -1 on error. */ int buf_isfull(BUFFER *buf) { if (buf == NULL) { errno = EINVAL; return (-1); } return (_buf_get_free_space(buf) == 0); /* XXX: positive value ? */ } /* * buf_util_space(): * Return no. of util bytes in the buffer, or -1 on error. */ int32_t buf_util_space(BUFFER *buf) { if (buf == NULL) { errno = EINVAL; return (-1); } return (_buf_get_util_space(buf)); } /* * buf_size(): * Return buffer size, or -1 on error. */ int32_t buf_size(BUFFER *buf) { if (buf == NULL) { errno = EINVAL; return (-1); } return (buf->buf_size); } /* * buf_put_raw(): * Attempt to store datasize bytes from data into the buffer; * return the no. of total bytes stored, or -1 on error. */ int32_t buf_put_raw(BUFFER *buf, const void *data, ssize_t datasize) { int32_t free_space, to_store; if (buf != NULL) { free_space = _buf_get_free_space(buf); to_store = (datasize > free_space) ? free_space : datasize; if (to_store) _buf_put_raw(buf, data, to_store); return (to_store); } errno = EINVAL; return (-1); } /* * buf_put_int32(): * Put a 32 bit number into the buffer (this number is converted * to network order before being stored); * return 4 on success (4 bytes stored), 0 if there are not * more space available, and -1 on error. */ int32_t buf_put_int32(BUFFER *buf, int32_t value) { if (buf != NULL) { if (_buf_get_free_space(buf) >= sizeof(value)) { value = htonl(value); _buf_put_raw(buf, &value, sizeof(value)); return (sizeof(value)); } return (0); } errno = EINVAL; return (-1); } /* * buf_get_raw(): * Attempt to retrieve datasize bytes from the buffer; * return the no. of total bytes stored, or -1 if error. */ int32_t buf_get_raw(BUFFER *buf, void *dst, ssize_t dstlen) { int32_t util_space, to_retrieve; if (buf != NULL) { util_space = _buf_get_util_space(buf); to_retrieve = (dstlen > util_space) ? util_space : dstlen; if (to_retrieve) _buf_get_raw(buf, dst, to_retrieve); return (to_retrieve); } errno = EINVAL; return (-1); } /* * buf_get_int32(): * Retrieve a 32 bit int value from the current position on * the buffer; return 4 on success (32 bits = 4 bytes), 0 if can't * exist such value on the buffer, and -1 on error. * NOTE: It is supposed the value on the buffer in network order * (the retrieved is converted to host order). */ int32_t buf_get_int32(BUFFER *buf, int32_t *value) { if (buf != NULL) { if (_buf_get_util_space(buf) >= sizeof(int32_t)) { _buf_get_raw(buf, value, sizeof(int32_t)); *value = ntohl(*value); return (sizeof(int32_t)); } return (0); } errno = EINVAL; return (-1); } /* * buf_compress(): * Compress a buffer; after compression, the first 4 bytes on the * destination buffer holds the original data lenght, and the * second 4 bytes the compressed data lenght (both in network order). * Return 0 on success, and -1 on error (if status is not NULL, * it contains the compress2(3) exit code). * NOTE 1: Destination buffer is automatically reset. * NOTE 2: The destination buffer must contain * 4 + 4 + source_buffer_length * (1 + 0.001) + 12 * bytes. The first two members holds the uncompressed and * compressed lengths, the rest is needed by the compress2 * function (see zlib manual). */ int buf_compress(BUFFER *dst, BUFFER *src, int level, int *status) { if (dst != NULL && src != NULL) { if (dst->buf_size > 8) { int st; uLongf compressed_len; compressed_len = dst->buf_size - 8; /* avail. space */ st = compress2(dst->buf_data + 8, &compressed_len, src->buf_data, src->buf_wpos, level); if (status != NULL) *status = st; switch(st) { case Z_OK: /* Save orignal and compressed lengths */ *(int32_t *) dst->buf_data = htonl(src->buf_wpos); *(int32_t *) (dst->buf_data + 4) = htonl((int32_t) compressed_len); dst->buf_rpos = 0; dst->buf_wpos = compressed_len + 8; return (0); case Z_MEM_ERROR: case Z_BUF_ERROR: errno = ENOMEM; break; case Z_STREAM_ERROR: errno = EINVAL; break; } } else { /* Destination buffer size <= 8 */ errno = EINVAL; if (status != NULL) *status = Z_BUF_ERROR; } } else /* Invalid dst and/or src value */ errno = EINVAL; return (-1); } /* * buf_uncompress(): * Uncompress a buffer; before uncompression, the first 8 bytes * on the source buffer should hold the uncompressed and compressed * data lenghts (in network order); Return 0 on success, -1 on * error, and, if status is not NULL, it contains the uncompress(3) * exit code (see zlib manual). * NOTE: Destination buffer is automatically reset. */ int buf_uncompress(BUFFER *dst, BUFFER *src, int *status) { int st; uLongf uncomp_len, comp_len; if (dst == NULL || src == NULL) { errno = EINVAL; return (-1); } if (src->buf_size <= 8) { errno = EINVAL; return (-1); } /* XXX: uLongf greater than or equal to int32_t ever ? */ uncomp_len = (uLongf) ntohl(*(int32_t *) src->buf_data); comp_len = (uLongf) ntohl(*(int32_t *) (src->buf_data + 4)); /* Destination buffer must have enough space */ if ((size_t) dst->buf_size < uncomp_len) { if (status != NULL) *status = Z_BUF_ERROR; errno = EINVAL; return (-1); } st = uncompress(dst->buf_data, &uncomp_len, src->buf_data + 8, comp_len); if (status != NULL) *status = st; switch (st) { case Z_OK: dst->buf_wpos = (int32_t) uncomp_len; /* < 2^31 ever */ dst->buf_rpos = 0; return(0); case Z_MEM_ERROR: case Z_BUF_ERROR: errno = ENOMEM; break; case Z_STREAM_ERROR: errno = EINVAL; break; } return (-1); } /* * buf_cipher(): * Encrypt/decrypt src buffer and put output on dst buffer; * NOTE 1: Destination buffer must have enough space. * NOTE 2: Don't need to reset destination buffer (it's done * automatically). */ void buf_cipher(BUFFER *dst, BUFFER *src, const EVP_CIPHER *type, unsigned char *key, int enc) { int len; EVP_CIPHER_CTX ctx; unsigned char iv[EVP_MAX_IV_LENGTH]; bzero(iv, sizeof(iv)); EVP_CipherInit(&ctx, type, key, iv, enc); dst->buf_wpos = dst->buf_size; EVP_CipherUpdate(&ctx, dst->buf_data, &dst->buf_wpos, src->buf_data, src->buf_wpos); len = dst->buf_size - dst->buf_wpos; EVP_CipherFinal(&ctx, dst->buf_data + dst->buf_wpos, &len); dst->buf_wpos += len; dst->buf_rpos = 0; } /* * buf_write(): * Write all util buffer data to the specified file descriptor; * on sucess return the no. of written bytes, or -1 on error. * NOTE 1: Calling this function don't means that all data was written. * NOTE 2: The file descriptor is a SOCKET on windows. */ #ifndef WIN32 int32_t buf_write(BUFFER *buf, int fd) #else int32_t buf_write(BUFFER *buf, SOCKET fd) #endif /* WIN32 */ { int32_t written, util_space; if (buf == NULL) { errno = EINVAL; return (-1); } util_space = _buf_get_util_space(buf); if (util_space) { #ifndef WIN32 written = write(fd, buf->buf_data + buf->buf_rpos, util_space); #else written = send(fd, buf->buf_data + buf->buf_rpos, util_space, 0); #endif /* WIN32 */ if (written > 0) buf->buf_rpos += written; } else written = 0; /* can't write if buffer haven't util data */ return (written); } /* * buf_read(); * Read data from the specified file descriptor and put it into * the free space on the buffer; * on success return the no. of readed bytes or -1 on error. * NOTE 1: It tries to read a no. of bytes that fills the whole * buffer. * NOTE 2: The file descriptor is a SOCKET on windows. */ #ifndef WIN32 int32_t buf_read(BUFFER *buf, int fd, int32_t size) #else int32_t buf_read(BUFFER *buf, SOCKET fd, int32_t size) #endif /* WIN32 */ { int32_t free_space, readed, total; if (buf == NULL) { errno = EINVAL; return (-1); } total = 0; free_space = _buf_get_free_space(buf); size = (free_space < size) ? free_space : size; while (size) { #ifndef WIN32 readed = read(fd, buf->buf_data + buf->buf_wpos, size); if (readed < 0) return (-1); #else readed = recv(fd, buf->buf_data + buf->buf_wpos, size, 0); if (readed == SOCKET_ERROR) return (-1); #endif /* WIN32 */ buf->buf_wpos += readed; size -= readed; total += readed; } return (total); }