openvpn/buffer.h
2008-06-11 21:59:26 +00:00

763 lines
18 KiB
C

/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-2008 OpenVPN Solutions LLC <info@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef BUFFER_H
#define BUFFER_H
#include "basic.h"
#include "thread.h"
/*
* Define verify_align function, otherwise
* it will be a noop.
*/
/* #define VERIFY_ALIGNMENT */
/*
* Keep track of source file/line of buf_init calls
*/
#ifdef VERIFY_ALIGNMENT
#define BUF_INIT_TRACKING
#endif
/* basic buffer class for OpenVPN */
struct buffer
{
int capacity; /* size of buffer allocated by malloc */
int offset; /* data starts at data + offset, offset > 0 to allow for efficient prepending */
int len; /* length of data that starts at data + offset */
uint8_t *data;
#ifdef BUF_INIT_TRACKING
const char *debug_file;
int debug_line;
#endif
};
/* for garbage collection */
struct gc_entry
{
struct gc_entry *next;
};
struct gc_arena
{
struct gc_entry *list;
};
#define BPTR(buf) ((buf)->data + (buf)->offset)
#define BEND(buf) (BPTR(buf) + (buf)->len)
#define BLAST(buf) (((buf)->data && (buf)->len) ? (BPTR(buf) + (buf)->len - 1) : NULL)
#define BLEN(buf) ((buf)->len)
#define BDEF(buf) ((buf)->data != NULL)
#define BSTR(buf) ((char *)BPTR(buf))
#define BCAP(buf) (buf_forward_capacity (buf))
void buf_clear (struct buffer *buf);
struct buffer clear_buf (void);
void free_buf (struct buffer *buf);
bool buf_assign (struct buffer *dest, const struct buffer *src);
void string_clear (char *str);
int string_array_len (const char **array);
#define PA_BRACKET (1<<0)
char *print_argv (const char **p, struct gc_arena *gc, const unsigned int flags);
/* for dmalloc debugging */
#ifdef DMALLOC
#define alloc_buf(size) alloc_buf_debug (size, __FILE__, __LINE__)
#define alloc_buf_gc(size, gc) alloc_buf_gc_debug (size, gc, __FILE__, __LINE__);
#define clone_buf(buf) clone_buf_debug (buf, __FILE__, __LINE__);
#define gc_malloc(size, clear, arena) gc_malloc_debug (size, clear, arena, __FILE__, __LINE__)
#define string_alloc(str, gc) string_alloc_debug (str, gc, __FILE__, __LINE__)
#define string_alloc_buf(str, gc) string_alloc_buf_debug (str, gc, __FILE__, __LINE__)
struct buffer alloc_buf_debug (size_t size, const char *file, int line);
struct buffer alloc_buf_gc_debug (size_t size, struct gc_arena *gc, const char *file, int line);
struct buffer clone_buf_debug (const struct buffer* buf, const char *file, int line);
void *gc_malloc_debug (size_t size, bool clear, struct gc_arena *a, const char *file, int line);
char *string_alloc_debug (const char *str, struct gc_arena *gc, const char *file, int line);
struct buffer string_alloc_buf_debug (const char *str, struct gc_arena *gc, const char *file, int line);
#else
struct buffer alloc_buf (size_t size);
struct buffer alloc_buf_gc (size_t size, struct gc_arena *gc); /* allocate buffer with garbage collection */
struct buffer clone_buf (const struct buffer* buf);
void *gc_malloc (size_t size, bool clear, struct gc_arena *a);
char *string_alloc (const char *str, struct gc_arena *gc);
struct buffer string_alloc_buf (const char *str, struct gc_arena *gc);
#endif
#ifdef BUF_INIT_TRACKING
#define buf_init(buf, offset) buf_init_debug (buf, offset, __FILE__, __LINE__)
bool buf_init_debug (struct buffer *buf, int offset, const char *file, int line);
#else
#define buf_init(buf, offset) buf_init_dowork (buf, offset)
#endif
/* inline functions */
static inline void
buf_reset (struct buffer *buf)
{
buf->capacity = 0;
buf->offset = 0;
buf->len = 0;
buf->data = NULL;
}
static inline void
buf_reset_len (struct buffer *buf)
{
buf->len = 0;
buf->offset = 0;
}
static inline bool
buf_init_dowork (struct buffer *buf, int offset)
{
if (offset < 0 || offset > buf->capacity || buf->data == NULL)
return false;
buf->len = 0;
buf->offset = offset;
return true;
}
static inline bool
buf_defined (struct buffer *buf)
{
return buf->data != NULL;
}
static inline void
buf_set_write (struct buffer *buf, uint8_t *data, int size)
{
buf->len = 0;
buf->offset = 0;
buf->capacity = size;
buf->data = data;
if (size > 0 && data)
*data = 0;
}
static inline void
buf_set_read (struct buffer *buf, const uint8_t *data, int size)
{
buf->len = buf->capacity = size;
buf->offset = 0;
buf->data = (uint8_t *)data;
}
/* Like strncpy but makes sure dest is always null terminated */
static inline void
strncpynt (char *dest, const char *src, size_t maxlen)
{
strncpy (dest, src, maxlen);
if (maxlen > 0)
dest[maxlen - 1] = 0;
}
/* return true if string contains at least one numerical digit */
static inline bool
has_digit (const unsigned char* src)
{
unsigned char c;
while ((c = *src++))
{
if (isdigit(c))
return true;
}
return false;
}
/*
* printf append to a buffer with overflow check
*/
void buf_printf (struct buffer *buf, const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
;
/*
* Like snprintf but guarantees null termination for size > 0
*/
int openvpn_snprintf(char *str, size_t size, const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 3, 4)))
#endif
;
/*
* remove/add trailing characters
*/
void buf_null_terminate (struct buffer *buf);
void buf_chomp (struct buffer *buf);
void buf_rmtail (struct buffer *buf, uint8_t remove);
/*
* non-buffer string functions
*/
void chomp (char *str);
void rm_trailing_chars (char *str, const char *what_to_delete);
const char *skip_leading_whitespace (const char *str);
void string_null_terminate (char *str, int len, int capacity);
/*
* Write string in buf to file descriptor fd.
* NOTE: requires that string be null terminated.
*/
void buf_write_string_file (const struct buffer *buf, const char *filename, int fd);
/*
* write a string to the end of a buffer that was
* truncated by buf_printf
*/
void buf_catrunc (struct buffer *buf, const char *str);
/*
* convert a multi-line output to one line
*/
void convert_to_one_line (struct buffer *buf);
/*
* Parse a string based on a given delimiter char
*/
bool buf_parse (struct buffer *buf, const int delim, char *line, const int size);
/*
* Hex dump -- Output a binary buffer to a hex string and return it.
*/
char *
format_hex_ex (const uint8_t *data, int size, int maxoutput,
int space_break, const char* separator,
struct gc_arena *gc);
static inline char *
format_hex (const uint8_t *data, int size, int maxoutput, struct gc_arena *gc)
{
return format_hex_ex (data, size, maxoutput, 4, " ", gc);
}
/*
* Return a buffer that is a subset of another buffer.
*/
struct buffer buf_sub (struct buffer *buf, int size, bool prepend);
/*
* Check if sufficient space to append to buffer.
*/
static inline bool
buf_safe (const struct buffer *buf, int len)
{
return len >= 0 && buf->offset + buf->len + len <= buf->capacity;
}
static inline bool
buf_safe_bidir (const struct buffer *buf, int len)
{
const int newlen = buf->len + len;
return newlen >= 0 && buf->offset + newlen <= buf->capacity;
}
static inline int
buf_forward_capacity (const struct buffer *buf)
{
int ret = buf->capacity - (buf->offset + buf->len);
if (ret < 0)
ret = 0;
return ret;
}
static inline int
buf_forward_capacity_total (const struct buffer *buf)
{
int ret = buf->capacity - buf->offset;
if (ret < 0)
ret = 0;
return ret;
}
static inline int
buf_reverse_capacity (const struct buffer *buf)
{
return buf->offset;
}
static inline bool
buf_inc_len (struct buffer *buf, int inc)
{
if (!buf_safe_bidir (buf, inc))
return false;
buf->len += inc;
return true;
}
/*
* Make space to prepend to a buffer.
* Return NULL if no space.
*/
static inline uint8_t *
buf_prepend (struct buffer *buf, int size)
{
if (size < 0 || size > buf->offset)
return NULL;
buf->offset -= size;
buf->len += size;
return BPTR (buf);
}
static inline bool
buf_advance (struct buffer *buf, int size)
{
if (size < 0 || buf->len < size)
return false;
buf->offset += size;
buf->len -= size;
return true;
}
/*
* Return a pointer to allocated space inside a buffer.
* Return NULL if no space.
*/
static inline uint8_t *
buf_write_alloc (struct buffer *buf, int size)
{
uint8_t *ret;
if (!buf_safe (buf, size))
return NULL;
ret = BPTR (buf) + buf->len;
buf->len += size;
return ret;
}
static inline uint8_t *
buf_write_alloc_prepend (struct buffer *buf, int size, bool prepend)
{
return prepend ? buf_prepend (buf, size) : buf_write_alloc (buf, size);
}
static inline uint8_t *
buf_read_alloc (struct buffer *buf, int size)
{
uint8_t *ret;
if (size < 0 || buf->len < size)
return NULL;
ret = BPTR (buf);
buf->offset += size;
buf->len -= size;
return ret;
}
static inline bool
buf_write (struct buffer *dest, const void *src, int size)
{
uint8_t *cp = buf_write_alloc (dest, size);
if (!cp)
return false;
memcpy (cp, src, size);
return true;
}
static inline bool
buf_write_prepend (struct buffer *dest, const void *src, int size)
{
uint8_t *cp = buf_prepend (dest, size);
if (!cp)
return false;
memcpy (cp, src, size);
return true;
}
static inline bool
buf_write_u8 (struct buffer *dest, int data)
{
uint8_t u8 = (uint8_t) data;
return buf_write (dest, &u8, sizeof (uint8_t));
}
static inline bool
buf_write_u16 (struct buffer *dest, int data)
{
uint16_t u16 = htons ((uint16_t) data);
return buf_write (dest, &u16, sizeof (uint16_t));
}
static inline bool
buf_write_u32 (struct buffer *dest, int data)
{
uint32_t u32 = htonl ((uint32_t) data);
return buf_write (dest, &u32, sizeof (uint32_t));
}
static inline bool
buf_copy (struct buffer *dest, const struct buffer *src)
{
return buf_write (dest, BPTR (src), BLEN (src));
}
static inline bool
buf_copy_n (struct buffer *dest, struct buffer *src, int n)
{
uint8_t *cp = buf_read_alloc (src, n);
if (!cp)
return false;
return buf_write (dest, cp, n);
}
static inline bool
buf_copy_range (struct buffer *dest,
int dest_index,
const struct buffer *src,
int src_index,
int src_len)
{
if (src_index < 0
|| src_len < 0
|| src_index + src_len > src->len
|| dest_index < 0
|| dest->offset + dest_index + src_len > dest->capacity)
return false;
memcpy (dest->data + dest->offset + dest_index, src->data + src->offset + src_index, src_len);
if (dest_index + src_len > dest->len)
dest->len = dest_index + src_len;
return true;
}
/* truncate src to len, copy excess data beyond len to dest */
static inline bool
buf_copy_excess (struct buffer *dest,
struct buffer *src,
int len)
{
if (len < 0)
return false;
if (src->len > len)
{
struct buffer b = *src;
src->len = len;
if (!buf_advance (&b, len))
return false;
return buf_copy (dest, &b);
}
else
{
return true;
}
}
static inline bool
buf_read (struct buffer *src, void *dest, int size)
{
uint8_t *cp = buf_read_alloc (src, size);
if (!cp)
return false;
memcpy (dest, cp, size);
return true;
}
static inline int
buf_read_u8 (struct buffer *buf)
{
int ret;
if (BLEN (buf) < 1)
return -1;
ret = *BPTR(buf);
buf_advance (buf, 1);
return ret;
}
static inline int
buf_read_u16 (struct buffer *buf)
{
uint16_t ret;
if (!buf_read (buf, &ret, sizeof (uint16_t)))
return -1;
return ntohs (ret);
}
static inline uint32_t
buf_read_u32 (struct buffer *buf, bool *good)
{
uint32_t ret;
if (!buf_read (buf, &ret, sizeof (uint32_t)))
{
if (good)
*good = false;
return 0;
}
else
{
if (good)
*good = true;
return ntohl (ret);
}
}
static inline bool
buf_string_match (const struct buffer *src, const void *match, int size)
{
if (size != src->len)
return false;
return memcmp (BPTR (src), match, size) == 0;
}
static inline bool
buf_string_match_head (const struct buffer *src, const void *match, int size)
{
if (size < 0 || size > src->len)
return false;
return memcmp (BPTR (src), match, size) == 0;
}
bool buf_string_match_head_str (const struct buffer *src, const char *match);
bool buf_string_compare_advance (struct buffer *src, const char *match);
int buf_substring_len (const struct buffer *buf, int delim);
/*
* Bitwise operations
*/
static inline void
xor (uint8_t *dest, const uint8_t *src, int len)
{
while (len-- > 0)
*dest++ ^= *src++;
}
/*
* Print a string which might be NULL
*/
const char *np (const char *str);
/*#define CHARACTER_CLASS_DEBUG*/
/* character classes */
#define CC_ANY (1<<0)
#define CC_NULL (1<<1)
#define CC_ALNUM (1<<2)
#define CC_ALPHA (1<<3)
#define CC_ASCII (1<<4)
#define CC_CNTRL (1<<5)
#define CC_DIGIT (1<<6)
#define CC_PRINT (1<<7)
#define CC_PUNCT (1<<8)
#define CC_SPACE (1<<9)
#define CC_XDIGIT (1<<10)
#define CC_BLANK (1<<11)
#define CC_NEWLINE (1<<12)
#define CC_CR (1<<13)
#define CC_BACKSLASH (1<<14)
#define CC_UNDERBAR (1<<15)
#define CC_DASH (1<<16)
#define CC_DOT (1<<17)
#define CC_COMMA (1<<18)
#define CC_COLON (1<<19)
#define CC_SLASH (1<<20)
#define CC_SINGLE_QUOTE (1<<21)
#define CC_DOUBLE_QUOTE (1<<22)
#define CC_REVERSE_QUOTE (1<<23)
#define CC_AT (1<<24)
#define CC_EQUAL (1<<25)
/* macro classes */
#define CC_NAME (CC_ALNUM|CC_UNDERBAR)
#define CC_CRLF (CC_CR|CC_NEWLINE)
bool char_class (const unsigned char c, const unsigned int flags);
bool string_class (const char *str, const unsigned int inclusive, const unsigned int exclusive);
bool string_mod (char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace);
const char *string_mod_const (const char *str,
const unsigned int inclusive,
const unsigned int exclusive,
const char replace,
struct gc_arena *gc);
#ifdef CHARACTER_CLASS_DEBUG
void character_class_debug (void);
#endif
/*
* Verify that a pointer is correctly aligned
*/
#ifdef VERIFY_ALIGNMENT
void valign4 (const struct buffer *buf, const char *file, const int line);
# define verify_align_4(ptr) valign4(buf, __FILE__, __LINE__)
#else
# define verify_align_4(ptr)
#endif
/*
* Very basic garbage collection, mostly for routines that return
* char ptrs to malloced strings.
*/
void gc_transfer (struct gc_arena *dest, struct gc_arena *src);
void x_gc_free (struct gc_arena *a);
static inline void
gc_init (struct gc_arena *a)
{
a->list = NULL;
}
static inline void
gc_detach (struct gc_arena *a)
{
gc_init (a);
}
static inline struct gc_arena
gc_new (void)
{
struct gc_arena ret;
ret.list = NULL;
return ret;
}
static inline void
gc_free (struct gc_arena *a)
{
if (a->list)
x_gc_free (a);
}
static inline void
gc_reset (struct gc_arena *a)
{
gc_free (a);
}
/*
* Allocate memory to hold a structure
*/
void out_of_memory (void);
#define ALLOC_OBJ(dptr, type) \
{ \
check_malloc_return ((dptr) = (type *) malloc (sizeof (type))); \
}
#define ALLOC_OBJ_CLEAR(dptr, type) \
{ \
ALLOC_OBJ (dptr, type); \
memset ((dptr), 0, sizeof(type)); \
}
#define ALLOC_ARRAY(dptr, type, n) \
{ \
check_malloc_return ((dptr) = (type *) malloc (sizeof (type) * (n))); \
}
#define ALLOC_ARRAY_GC(dptr, type, n, gc) \
{ \
(dptr) = (type *) gc_malloc (sizeof (type) * (n), false, (gc)); \
}
#define ALLOC_ARRAY_CLEAR(dptr, type, n) \
{ \
ALLOC_ARRAY (dptr, type, n); \
memset ((dptr), 0, (sizeof(type) * (n))); \
}
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \
{ \
(dptr) = (type *) gc_malloc (sizeof (type) * (n), true, (gc)); \
}
#define ALLOC_OBJ_GC(dptr, type, gc) \
{ \
(dptr) = (type *) gc_malloc (sizeof (type), false, (gc)); \
}
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc) \
{ \
(dptr) = (type *) gc_malloc (sizeof (type), true, (gc)); \
}
static inline void
check_malloc_return (void *p)
{
void out_of_memory (void);
if (!p)
out_of_memory ();
}
/*
* Manage lists of buffers
*/
#ifdef ENABLE_BUFFER_LIST
struct buffer_entry
{
struct buffer buf;
struct buffer_entry *next;
};
struct buffer_list
{
struct buffer_entry *head; /* next item to pop/peek */
struct buffer_entry *tail; /* last item pushed */
int size; /* current number of entries */
int max_size; /* maximum size list should grow to */
};
struct buffer_list *buffer_list_new (const int max_size);
void buffer_list_free (struct buffer_list *ol);
bool buffer_list_defined (const struct buffer_list *ol);
void buffer_list_reset (struct buffer_list *ol);
void buffer_list_push (struct buffer_list *ol, const unsigned char *str);
const struct buffer *buffer_list_peek (struct buffer_list *ol);
void buffer_list_advance (struct buffer_list *ol, int n);
struct buffer_list *buffer_list_file (const char *fn, int max_line_len);
#endif
#endif /* BUFFER_H */