Codebase list dnscat2 / 9b31863 client / libs / buffer.h
9b31863

Tree @9b31863 (Download .tar.gz)

buffer.h @9b31863raw · history · blame

/* buffer.h
 * By Ron
 * Created August, 2008
 *
 * (See LICENSE.md)
 *
 * This is a generic class for buffering (marshalling) data that in many ways
 * dates back to my days as a Battle.net developers. It gives programmers an
 * easy way to prepare data to be sent on the network, as well as a simplified
 * way to build strings in a language where string building isn't always
 * straight forward. In many ways, it's like the pack()/unpack() functions from
 * Perl.
 *
 * I've strived to keep this implementation the same on every platform. You'll
 * notice that there's no Windows-specific code here, and also that all
 * calculations will work on both a little- or big-endian system. The only time
 * you won't get the same results is when you create a buffer with the type
 * BO_HOST, which always uses the host's byte ordering. I don't recommend that.
 */

#ifndef __BUFFER_H__
#define __BUFFER_H__

#include <stdlib.h> /* For "size_t". */

#include "types.h"

typedef enum
{
  BO_HOST,          /* Use the byte order of the host (bad idea -- changes on systems. */
  BO_NETWORK,       /* Use network byte order which, as it turns out, is big endian. */
  BO_LITTLE_ENDIAN, /* Use big endian byte ordering (0x12345678 => 78 56 34 12). */
  BO_BIG_ENDIAN,    /* Use big endian byte ordering (0x12345678 => 12 34 56 78). */
} BYTE_ORDER_t;

/* This struct shouldn't be accessed directly */
typedef struct
{
  /* Byte order to use */
  BYTE_ORDER_t byte_order;

  /* The current position in the string, used when reading it. */
  size_t position;

  /* The maximum length of the buffer that "buffer" is pointing to.  When
   * space in this runs out, it's expanded  */
  size_t max_length;

  /* The current length of the buffer. */
  size_t current_length;

  /* The current buffer.  Will always point to a string of length max_length */
  uint8_t *data;

  /* Set to FALSE when the packet is destroyed, to make sure I don't accidentally
   * re-use it (again) */
  NBBOOL valid;

} buffer_t;

/* Create a new packet buffer */
buffer_t *buffer_create(BYTE_ORDER_t byte_order);

/* Create a new packet buffer, with data. */
buffer_t *buffer_create_with_data(BYTE_ORDER_t byte_order, const void *data, const size_t length);

/* Go to the start of the buffer. */
void buffer_reset(buffer_t *buffer);

/* Destroy the buffer and free resources.  If this isn't used, memory will leak. */
void buffer_destroy(buffer_t *buffer);

/* Makes a copy of the buffer. */
buffer_t *buffer_duplicate(buffer_t *base);

/* Get the length of the buffer. */
size_t buffer_get_length(buffer_t *buffer);

/* Get the current location in the buffer. */
size_t buffer_get_current_offset(buffer_t *buffer);
/* Set the current location in the buffer. */
void buffer_set_current_offset(buffer_t *buffer, size_t position);

/* Get the number of bytes between the current offset and the end of the buffer. */
size_t buffer_get_remaining_bytes(buffer_t *buffer);

/* Clear out the buffer. Memory is kept, but the contents are blanked out and the pointer is returned
 * to the beginning. */
void buffer_clear(buffer_t *buffer);

/* Align the buffer to even multiples. */
void buffer_read_align(buffer_t *buffer, size_t align);
void buffer_write_align(buffer_t *buffer, size_t align);

/* Consume (discard) bytes. */
void buffer_consume(buffer_t *buffer, size_t count);

/* Return the contents of the buffer in a newly allocated string. Fill in the length, if a pointer
 * is given. Note that this allocates memory that has to be freed! */
uint8_t *buffer_create_string(buffer_t *buffer, size_t *length);
/* Does the same thing as above, but also frees up the buffer (good for a function return). */
uint8_t *buffer_create_string_and_destroy(buffer_t *buffer, size_t *length);

/* Returns the contents of the buffer - starting at the current position - in a newly allocated
 * string. Returns the length in the length pointer. If max_bytes is -1, all bytes are returned. */
uint8_t *buffer_read_remaining_bytes(buffer_t *buffer, size_t *length, size_t max_bytes, NBBOOL consume);

/* Add data to the end of the buffer */
buffer_t *buffer_add_int8(buffer_t *buffer,      const uint8_t data);
buffer_t *buffer_add_int16(buffer_t *buffer,     const uint16_t data);
buffer_t *buffer_add_int32(buffer_t *buffer,     const uint32_t data);
buffer_t *buffer_add_ntstring(buffer_t *buffer,  const char *data);
buffer_t *buffer_add_string(buffer_t *buffer,    const char *data);
/* Note: UNICODE support is a hack -- it adds every second character as a NULL, but is otherwise ASCII. */
buffer_t *buffer_add_unicode(buffer_t *buffer,   const char *data);
buffer_t *buffer_add_bytes(buffer_t *buffer,     const void *data, const size_t length);
buffer_t *buffer_add_buffer(buffer_t *buffer,    const buffer_t *source);

/* Add data to the middle of a buffer. These functions won't write past the end of the buffer, so it's
 * up to the programmer to be careful. */
buffer_t *buffer_add_int8_at(buffer_t *buffer,      const uint8_t data, size_t offset);
buffer_t *buffer_add_int16_at(buffer_t *buffer,     const uint16_t data, size_t offset);
buffer_t *buffer_add_int32_at(buffer_t *buffer,     const uint32_t data, size_t offset);
buffer_t *buffer_add_ntstring_at(buffer_t *buffer,  const char *data, size_t offset);
buffer_t *buffer_add_string_at(buffer_t *buffer,    const char *data, size_t offset);
buffer_t *buffer_add_unicode_at(buffer_t *buffer,   const char *data, size_t offset);
buffer_t *buffer_add_bytes_at(buffer_t *buffer,     const void *data, const size_t length, size_t offset);
buffer_t *buffer_add_buffer_at(buffer_t *buffer,    const buffer_t *source, size_t offset);

/* Read the next data from the buffer.  The first read will be at the beginning.
 * An assertion will fail and the program will end if read off
 * the end of the buffer; it's probably a good idea to verify that enough data can be removed
 * before actually attempting to remove it; otherwise, a DoS condition can occur */
uint8_t   buffer_read_next_int8(buffer_t *buffer);
uint16_t  buffer_read_next_int16(buffer_t *buffer);
uint32_t  buffer_read_next_int32(buffer_t *buffer);
char     *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, size_t max_length);
char     *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, size_t max_length);
char     *buffer_read_next_unicode_data(buffer_t *buffer, char *data_ret, size_t length);
void     *buffer_read_next_bytes(buffer_t *buffer, void *data, size_t length);

/* Allocate memory to hold the result. */
char     *buffer_alloc_next_ntstring(buffer_t *buffer);

/* Read the next data, without incrementing the current pointer. */
uint8_t   buffer_peek_next_int8(buffer_t *buffer);
uint16_t  buffer_peek_next_int16(buffer_t *buffer);
uint32_t  buffer_peek_next_int32(buffer_t *buffer);
char     *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, size_t max_length);
char     *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, size_t max_length);
void     *buffer_peek_next_bytes(buffer_t *buffer, void *data, size_t length);

/* Read data at the specified location in the buffer (counting the first byte as 0). */
uint8_t   buffer_read_int8_at(buffer_t *buffer, size_t offset);
uint16_t  buffer_read_int16_at(buffer_t *buffer, size_t offset);
uint32_t  buffer_read_int32_at(buffer_t *buffer, size_t offset);
char     *buffer_read_ntstring_at(buffer_t *buffer, size_t offset, char *data_ret, size_t max_length);
char     *buffer_read_unicode_at(buffer_t *buffer, size_t offset, char *data_ret, size_t max_length);
char     *buffer_read_unicode_data_at(buffer_t *buffer, size_t offset, char *data_ret, size_t length);
void     *buffer_read_bytes_at(buffer_t *buffer, size_t offset, void *data, size_t length);

/* Allocate memory to hold the result. */
char *buffer_alloc_ntstring_at(buffer_t *buffer, size_t offset);

/* These NBBOOL functions check if there are enough bytes left in the buffer to remove
 * specified data.  These should always be used on the server side to verify valid
 * packets for a critical service. */
NBBOOL buffer_can_read_int8(buffer_t *buffer);
NBBOOL buffer_can_read_int16(buffer_t *buffer);
NBBOOL buffer_can_read_int32(buffer_t *buffer);
NBBOOL buffer_can_read_ntstring(buffer_t *buffer);
NBBOOL buffer_can_read_unicode(buffer_t *buffer);
NBBOOL buffer_can_read_bytes(buffer_t *buffer, size_t length);

/* These functions check if there are enough bytes in the buffer at the specified location. */
NBBOOL buffer_can_read_int8_at(buffer_t *buffer, size_t offset);
NBBOOL buffer_can_read_int16_at(buffer_t *buffer, size_t offset);
NBBOOL buffer_can_read_int32_at(buffer_t *buffer, size_t offset);
NBBOOL buffer_can_read_ntstring_at(buffer_t *buffer, size_t offset, size_t max_length);
NBBOOL buffer_can_read_unicode_at(buffer_t *buffer, size_t offset, size_t max_length);
NBBOOL buffer_can_read_bytes_at(buffer_t *buffer, size_t offset, size_t length);

/* Print out the buffer in a nice format -- useful for debugging. */
void buffer_print(buffer_t *buffer);

/* Returns a pointer to the actual buffer (I don't recommend using this). */
uint8_t *buffer_get(buffer_t *buffer, size_t *length);

#endif