6fbf66fa |
/*
* 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.
* |
58716979 |
* Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> |
6fbf66fa |
*
* 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.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
#ifndef BUFFER_H
#define BUFFER_H
#include "basic.h" |
2b7deeb6 |
#include "error.h" |
6fbf66fa |
|
b551bec9 |
#define BUF_SIZE_MAX 1000000
|
6fbf66fa |
/*
* 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
|
5959e9de |
/**************************************************************************/
/**
* Wrapper structure for dynamically allocated memory.
*
* The actual content stored in a \c buffer structure starts at the memory
* location \c buffer.data \c + \c buffer.offset, and has a length of \c
* buffer.len bytes. This, together with the space available before and
* after the content, is represented in the pseudocode below: |
81d882d5 |
* @code
* uint8_t *content_start = buffer.data + buffer.offset;
* uint8_t *content_end = buffer.data + buffer.offset + buffer.len;
* int prepend_capacity = buffer.offset;
* int append_capacity = buffer.capacity - (buffer.offset + buffer.len);
* @endcode |
5959e9de |
*/ |
6fbf66fa |
struct buffer
{ |
81d882d5 |
int capacity; /**< Size in bytes of memory allocated by |
5959e9de |
* \c malloc(). */ |
81d882d5 |
int offset; /**< Offset in bytes of the actual content |
5959e9de |
* within the allocated memory. */ |
81d882d5 |
int len; /**< Length in bytes of the actual content |
5959e9de |
* within the allocated memory. */ |
81d882d5 |
uint8_t *data; /**< Pointer to the allocated memory. */ |
6fbf66fa |
#ifdef BUF_INIT_TRACKING |
81d882d5 |
const char *debug_file;
int debug_line; |
6fbf66fa |
#endif
};
|
5959e9de |
/**************************************************************************/
/**
* Garbage collection entry for one dynamically allocated block of memory.
*
* This structure represents one link in the linked list contained in a \c
* gc_arena structure. Each time the \c gc_malloc() function is called,
* it allocates \c sizeof(gc_entry) + the requested number of bytes. The
* \c gc_entry is then stored as a header in front of the memory address
* returned to the caller.
*/ |
6fbf66fa |
struct gc_entry
{ |
81d882d5 |
struct gc_entry *next; /**< Pointer to the next item in the |
5959e9de |
* linked list. */ |
6fbf66fa |
};
|
e719a053 |
/** |
42d9f324 |
* Garbage collection entry for a specially allocated structure that needs |
e719a053 |
* a custom free function to be freed like struct addrinfo
*
*/
struct gc_entry_special
{ |
81d882d5 |
struct gc_entry_special *next;
void (*free_fnc)(void *);
void *addr; |
e719a053 |
};
|
5959e9de |
/**
* Garbage collection arena used to keep track of dynamically allocated
* memory.
*
* This structure contains a linked list of \c gc_entry structures. When
* a block of memory is allocated using the \c gc_malloc() function, the
* allocation is registered in the function's \c gc_arena argument. All
* the dynamically allocated memory registered in a \c gc_arena can be
* freed using the \c gc_free() function.
*/ |
6fbf66fa |
struct gc_arena
{ |
81d882d5 |
struct gc_entry *list; /**< First element of the linked list of |
5959e9de |
* \c gc_entry structures. */ |
81d882d5 |
struct gc_entry_special *list_special; |
6fbf66fa |
};
|
5959e9de |
|
b551bec9 |
#define BPTR(buf) (buf_bptr(buf))
#define BEND(buf) (buf_bend(buf))
#define BLAST(buf) (buf_blast(buf))
#define BLEN(buf) (buf_len(buf))
#define BDEF(buf) (buf_defined(buf))
#define BSTR(buf) (buf_str(buf)) |
81d882d5 |
#define BCAP(buf) (buf_forward_capacity(buf)) |
6fbf66fa |
|
81d882d5 |
void buf_clear(struct buffer *buf); |
6fbf66fa |
|
81d882d5 |
void free_buf(struct buffer *buf); |
6fbf66fa |
|
81d882d5 |
bool buf_assign(struct buffer *dest, const struct buffer *src); |
eadf16a6 |
|
81d882d5 |
void string_clear(char *str);
int string_array_len(const char **array);
size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra); |
8e986316 |
|
eadf16a6 |
#define PA_BRACKET (1<<0) |
81d882d5 |
char *print_argv(const char **p, struct gc_arena *gc, const unsigned int flags); |
6fbf66fa |
|
81d882d5 |
void buf_size_error(const size_t size); |
b551bec9 |
|
6fbf66fa |
/* for dmalloc debugging */
#ifdef DMALLOC
|
81d882d5 |
#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__) |
6fbf66fa |
|
81d882d5 |
struct buffer alloc_buf_debug(size_t size, const char *file, int line); |
6fbf66fa |
|
81d882d5 |
struct buffer alloc_buf_gc_debug(size_t size, struct gc_arena *gc, const char *file, int line); |
6fbf66fa |
|
81d882d5 |
struct buffer clone_buf_debug(const struct buffer *buf, const char *file, int line); |
6fbf66fa |
|
81d882d5 |
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 /* ifdef DMALLOC */
struct buffer alloc_buf(size_t size);
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc); /* allocate buffer with garbage collection */ |
6fbf66fa |
|
81d882d5 |
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 DMALLOC */
void gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a); |
e719a053 |
|
6fbf66fa |
#ifdef BUF_INIT_TRACKING |
81d882d5 |
#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);
|
6fbf66fa |
#else |
81d882d5 |
#define buf_init(buf, offset) buf_init_dowork(buf, offset) |
6fbf66fa |
#endif
/* inline functions */ |
e719a053 |
inline static void |
81d882d5 |
gc_freeaddrinfo_callback(void *addr) |
e719a053 |
{ |
81d882d5 |
freeaddrinfo((struct addrinfo *) addr); |
e719a053 |
} |
6fbf66fa |
|
3280c4c2 |
/** Return an empty struct buffer */
static inline struct buffer
clear_buf(void)
{
return (struct buffer) { 0 };
}
|
b551bec9 |
static inline bool |
81d882d5 |
buf_defined(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
return buf->data != NULL; |
b551bec9 |
}
static inline bool |
81d882d5 |
buf_valid(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
return likely(buf->data != NULL) && likely(buf->len >= 0); |
b551bec9 |
}
static inline uint8_t * |
81d882d5 |
buf_bptr(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
if (buf_valid(buf))
{
return buf->data + buf->offset;
}
else
{
return NULL;
} |
b551bec9 |
}
static int |
81d882d5 |
buf_len(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
if (buf_valid(buf))
{
return buf->len;
}
else
{
return 0;
} |
b551bec9 |
}
static inline uint8_t * |
81d882d5 |
buf_bend(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
return buf_bptr(buf) + buf_len(buf); |
b551bec9 |
}
static inline uint8_t * |
81d882d5 |
buf_blast(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
if (buf_len(buf) > 0)
{
return buf_bptr(buf) + buf_len(buf) - 1;
}
else
{
return NULL;
} |
b551bec9 |
}
static inline bool |
81d882d5 |
buf_size_valid(const size_t size) |
b551bec9 |
{ |
81d882d5 |
return likely(size < BUF_SIZE_MAX); |
b551bec9 |
}
static inline bool |
81d882d5 |
buf_size_valid_signed(const int size) |
b551bec9 |
{ |
81d882d5 |
return likely(size >= -BUF_SIZE_MAX) && likely(size < BUF_SIZE_MAX); |
b551bec9 |
}
static inline char * |
81d882d5 |
buf_str(const struct buffer *buf) |
b551bec9 |
{ |
81d882d5 |
return (char *)buf_bptr(buf); |
b551bec9 |
}
|
6fbf66fa |
static inline void |
81d882d5 |
buf_reset(struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
buf->capacity = 0;
buf->offset = 0;
buf->len = 0;
buf->data = NULL; |
6fbf66fa |
}
|
47ae8457 |
static inline void |
81d882d5 |
buf_reset_len(struct buffer *buf) |
47ae8457 |
{ |
81d882d5 |
buf->len = 0;
buf->offset = 0; |
47ae8457 |
}
|
6fbf66fa |
static inline bool |
81d882d5 |
buf_init_dowork(struct buffer *buf, int offset) |
6fbf66fa |
{ |
81d882d5 |
if (offset < 0 || offset > buf->capacity || buf->data == NULL)
{
return false;
}
buf->len = 0;
buf->offset = offset;
return true; |
6fbf66fa |
}
static inline void |
81d882d5 |
buf_set_write(struct buffer *buf, uint8_t *data, int size) |
6fbf66fa |
{ |
81d882d5 |
if (!buf_size_valid(size))
{
buf_size_error(size);
}
buf->len = 0;
buf->offset = 0;
buf->capacity = size;
buf->data = data;
if (size > 0 && data)
{
*data = 0;
} |
6fbf66fa |
}
static inline void |
81d882d5 |
buf_set_read(struct buffer *buf, const uint8_t *data, int size) |
6fbf66fa |
{ |
81d882d5 |
if (!buf_size_valid(size))
{
buf_size_error(size);
}
buf->len = buf->capacity = size;
buf->offset = 0;
buf->data = (uint8_t *)data; |
6fbf66fa |
}
/* Like strncpy but makes sure dest is always null terminated */
static inline void |
81d882d5 |
strncpynt(char *dest, const char *src, size_t maxlen) |
6fbf66fa |
{ |
81d882d5 |
strncpy(dest, src, maxlen);
if (maxlen > 0)
{
dest[maxlen - 1] = 0;
} |
6fbf66fa |
}
/* return true if string contains at least one numerical digit */
static inline bool |
81d882d5 |
has_digit(const unsigned char *src) |
6fbf66fa |
{ |
81d882d5 |
unsigned char c;
while ((c = *src++)) |
6fbf66fa |
{ |
81d882d5 |
if (isdigit(c))
{
return true;
} |
6fbf66fa |
} |
81d882d5 |
return false; |
6fbf66fa |
}
|
009521ac |
/**
* Securely zeroise memory.
*
* This code and description are based on code supplied by Zhaomo Yang, of the
* University of California, San Diego (which was released into the public
* domain).
*
* The secure_memzero function attempts to ensure that an optimizing compiler
* does not remove the intended operation if cleared memory is not accessed
* again by the program. This code has been tested under Clang 3.9.0 and GCC
* 6.2 with optimization flags -O, -Os, -O0, -O1, -O2, and -O3 on
* Ubuntu 16.04.1 LTS; under Clang 3.9.0 with optimization flags -O, -Os,
* -O0, -O1, -O2, and -O3 on FreeBSD 10.2-RELEASE; under Microsoft Visual Studio
* 2015 with optimization flags /O1, /O2 and /Ox on Windows 10.
*
* Theory of operation:
*
* 1. On Windows, use the SecureZeroMemory which ensures that data is
* overwritten.
* 2. Under GCC or Clang, use a memory barrier, which forces the preceding
* memset to be carried out. The overhead of a memory barrier is usually
* negligible.
* 3. If none of the above are available, use the volatile pointer
* technique to zero memory one byte at a time.
* |
81d882d5 |
* @param data Pointer to data to zeroise.
* @param len Length of data, in bytes. |
009521ac |
*/
static inline void |
81d882d5 |
secure_memzero(void *data, size_t len) |
009521ac |
{
#if defined(_WIN32) |
81d882d5 |
SecureZeroMemory(data, len); |
009521ac |
#elif defined(__GNUC__) || defined(__clang__) |
81d882d5 |
memset(data, 0, len);
__asm__ __volatile__ ("" : : "r" (data) : "memory"); |
009521ac |
#else |
81d882d5 |
volatile char *p = (volatile char *) data;
while (len--) |
4cd4899e |
{ |
81d882d5 |
*p++ = 0; |
4cd4899e |
} |
009521ac |
#endif
}
|
6fbf66fa |
/* |
d40cbf0e |
* printf append to a buffer with overflow check,
* due to usage of vsnprintf, it will leave space for
* a final null character and thus use only
* capacity - 1 |
6fbf66fa |
*/ |
81d882d5 |
bool buf_printf(struct buffer *buf, const char *format, ...) |
6fbf66fa |
#ifdef __GNUC__ |
ce386278 |
#if __USE_MINGW_ANSI_STDIO |
81d882d5 |
__attribute__ ((format(gnu_printf, 2, 3))) |
ce386278 |
#else |
81d882d5 |
__attribute__ ((format(__printf__, 2, 3))) |
ce386278 |
#endif |
6fbf66fa |
#endif |
81d882d5 |
; |
6fbf66fa |
/* |
4d3df224 |
* puts append to a buffer with overflow check
*/ |
81d882d5 |
bool buf_puts(struct buffer *buf, const char *str); |
4d3df224 |
/* |
6fbf66fa |
* Like snprintf but guarantees null termination for size > 0
*/ |
d5497262 |
bool openvpn_snprintf(char *str, size_t size, const char *format, ...) |
6fbf66fa |
#ifdef __GNUC__ |
ce386278 |
#if __USE_MINGW_ANSI_STDIO |
81d882d5 |
__attribute__ ((format(gnu_printf, 3, 4))) |
ce386278 |
#else |
81d882d5 |
__attribute__ ((format(__printf__, 3, 4))) |
ce386278 |
#endif |
6fbf66fa |
#endif |
81d882d5 |
; |
6fbf66fa |
/*
* remove/add trailing characters
*/
|
81d882d5 |
void buf_null_terminate(struct buffer *buf);
void buf_chomp(struct buffer *buf);
void buf_rmtail(struct buffer *buf, uint8_t remove); |
6fbf66fa |
/*
* non-buffer string functions
*/ |
81d882d5 |
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); |
6fbf66fa |
/*
* Write string in buf to file descriptor fd.
* NOTE: requires that string be null terminated.
*/ |
81d882d5 |
void buf_write_string_file(const struct buffer *buf, const char *filename, int fd); |
6fbf66fa |
/*
* write a string to the end of a buffer that was
* truncated by buf_printf
*/ |
81d882d5 |
void buf_catrunc(struct buffer *buf, const char *str); |
6fbf66fa |
/*
* convert a multi-line output to one line
*/ |
81d882d5 |
void convert_to_one_line(struct buffer *buf); |
6fbf66fa |
/*
* Parse a string based on a given delimiter char
*/ |
81d882d5 |
bool buf_parse(struct buffer *buf, const int delim, char *line, const int size); |
6fbf66fa |
/*
* Hex dump -- Output a binary buffer to a hex string and return it.
*/ |
0a6a8015 |
#define FHE_SPACE_BREAK_MASK 0xFF /* space_break parameter in lower 8 bits */
#define FHE_CAPS 0x100 /* output hex in caps */ |
6fbf66fa |
char * |
81d882d5 |
format_hex_ex(const uint8_t *data, int size, int maxoutput,
unsigned int space_break_flags, const char *separator,
struct gc_arena *gc); |
6fbf66fa |
static inline char * |
81d882d5 |
format_hex(const uint8_t *data, int size, int maxoutput, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
return format_hex_ex(data, size, maxoutput, 4, " ", gc); |
6fbf66fa |
}
/*
* Return a buffer that is a subset of another buffer.
*/ |
81d882d5 |
struct buffer buf_sub(struct buffer *buf, int size, bool prepend); |
6fbf66fa |
/*
* Check if sufficient space to append to buffer.
*/
static inline bool |
81d882d5 |
buf_safe(const struct buffer *buf, int len) |
6fbf66fa |
{ |
81d882d5 |
return buf_valid(buf) && buf_size_valid(len)
&& buf->offset + buf->len + len <= buf->capacity; |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_safe_bidir(const struct buffer *buf, int len) |
6fbf66fa |
{ |
81d882d5 |
if (buf_valid(buf) && buf_size_valid_signed(len)) |
b551bec9 |
{ |
81d882d5 |
const int newlen = buf->len + len;
return newlen >= 0 && buf->offset + newlen <= buf->capacity;
}
else
{
return false; |
b551bec9 |
} |
6fbf66fa |
}
static inline int |
81d882d5 |
buf_forward_capacity(const struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
if (buf_valid(buf))
{
int ret = buf->capacity - (buf->offset + buf->len);
if (ret < 0)
{
ret = 0;
}
return ret;
}
else |
b551bec9 |
{ |
81d882d5 |
return 0; |
b551bec9 |
} |
6fbf66fa |
}
static inline int |
81d882d5 |
buf_forward_capacity_total(const struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
if (buf_valid(buf))
{
int ret = buf->capacity - buf->offset;
if (ret < 0)
{
ret = 0;
}
return ret;
}
else |
b551bec9 |
{ |
81d882d5 |
return 0; |
b551bec9 |
} |
6fbf66fa |
}
static inline int |
81d882d5 |
buf_reverse_capacity(const struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
if (buf_valid(buf))
{
return buf->offset;
}
else
{
return 0;
} |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_inc_len(struct buffer *buf, int inc) |
6fbf66fa |
{ |
81d882d5 |
if (!buf_safe_bidir(buf, inc))
{
return false;
}
buf->len += inc;
return true; |
6fbf66fa |
}
/*
* Make space to prepend to a buffer.
* Return NULL if no space.
*/
static inline uint8_t * |
81d882d5 |
buf_prepend(struct buffer *buf, int size) |
6fbf66fa |
{ |
81d882d5 |
if (!buf_valid(buf) || size < 0 || size > buf->offset)
{
return NULL;
}
buf->offset -= size;
buf->len += size;
return BPTR(buf); |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_advance(struct buffer *buf, int size) |
6fbf66fa |
{ |
81d882d5 |
if (!buf_valid(buf) || size < 0 || buf->len < size)
{
return false;
}
buf->offset += size;
buf->len -= size;
return true; |
6fbf66fa |
}
/*
* Return a pointer to allocated space inside a buffer.
* Return NULL if no space.
*/
static inline uint8_t * |
81d882d5 |
buf_write_alloc(struct buffer *buf, int size) |
6fbf66fa |
{ |
81d882d5 |
uint8_t *ret;
if (!buf_safe(buf, size))
{
return NULL;
}
ret = BPTR(buf) + buf->len;
buf->len += size;
return ret; |
6fbf66fa |
}
static inline uint8_t * |
81d882d5 |
buf_write_alloc_prepend(struct buffer *buf, int size, bool prepend) |
6fbf66fa |
{ |
81d882d5 |
return prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size); |
6fbf66fa |
}
static inline uint8_t * |
81d882d5 |
buf_read_alloc(struct buffer *buf, int size) |
6fbf66fa |
{ |
81d882d5 |
uint8_t *ret;
if (size < 0 || buf->len < size)
{
return NULL;
}
ret = BPTR(buf);
buf->offset += size;
buf->len -= size;
return ret; |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_write(struct buffer *dest, const void *src, int size) |
6fbf66fa |
{ |
81d882d5 |
uint8_t *cp = buf_write_alloc(dest, size);
if (!cp)
{
return false;
}
memcpy(cp, src, size);
return true; |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_write_prepend(struct buffer *dest, const void *src, int size) |
6fbf66fa |
{ |
81d882d5 |
uint8_t *cp = buf_prepend(dest, size);
if (!cp)
{
return false;
}
memcpy(cp, src, size);
return true; |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_write_u8(struct buffer *dest, int data) |
6fbf66fa |
{ |
81d882d5 |
uint8_t u8 = (uint8_t) data;
return buf_write(dest, &u8, sizeof(uint8_t)); |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_write_u16(struct buffer *dest, int data) |
6fbf66fa |
{ |
81d882d5 |
uint16_t u16 = htons((uint16_t) data);
return buf_write(dest, &u16, sizeof(uint16_t)); |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_write_u32(struct buffer *dest, int data) |
6fbf66fa |
{ |
81d882d5 |
uint32_t u32 = htonl((uint32_t) data);
return buf_write(dest, &u32, sizeof(uint32_t)); |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_copy(struct buffer *dest, const struct buffer *src) |
6fbf66fa |
{ |
81d882d5 |
return buf_write(dest, BPTR(src), BLEN(src)); |
6fbf66fa |
}
static inline bool |
81d882d5 |
buf_copy_n(struct buffer *dest, struct buffer *src, int n) |
6fbf66fa |
{ |
81d882d5 |
uint8_t *cp = buf_read_alloc(src, n);
if (!cp)
{
return false;
}
return buf_write(dest, cp, n); |
6fbf66fa |
}
static inline bool |
81d882d5 |
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; |
6fbf66fa |
}
/* truncate src to len, copy excess data beyond len to dest */
static inline bool |
81d882d5 |
buf_copy_excess(struct buffer *dest,
struct buffer *src,
int len) |
6fbf66fa |
{ |
81d882d5 |
if (len < 0)
{
return false;
}
if (src->len > len) |
6fbf66fa |
{ |
81d882d5 |
struct buffer b = *src;
src->len = len;
if (!buf_advance(&b, len))
{
return false;
}
return buf_copy(dest, &b); |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
return true; |
6fbf66fa |
}
}
static inline bool |
81d882d5 |
buf_read(struct buffer *src, void *dest, int size) |
6fbf66fa |
{ |
81d882d5 |
uint8_t *cp = buf_read_alloc(src, size);
if (!cp)
{
return false;
}
memcpy(dest, cp, size);
return true; |
6fbf66fa |
}
static inline int |
81d882d5 |
buf_read_u8(struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
int ret;
if (BLEN(buf) < 1)
{
return -1;
}
ret = *BPTR(buf);
buf_advance(buf, 1);
return ret; |
6fbf66fa |
}
static inline int |
81d882d5 |
buf_read_u16(struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
uint16_t ret;
if (!buf_read(buf, &ret, sizeof(uint16_t)))
{
return -1;
}
return ntohs(ret); |
6fbf66fa |
}
static inline uint32_t |
81d882d5 |
buf_read_u32(struct buffer *buf, bool *good) |
6fbf66fa |
{ |
81d882d5 |
uint32_t ret;
if (!buf_read(buf, &ret, sizeof(uint32_t))) |
6fbf66fa |
{ |
81d882d5 |
if (good)
{
*good = false;
}
return 0; |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
if (good)
{
*good = true;
}
return ntohl(ret); |
6fbf66fa |
}
}
|
11d21349 |
/**
* Compare src buffer contents with match.
* *NOT* constant time. Do not use when comparing HMACs.
*/ |
6fbf66fa |
static inline bool |
81d882d5 |
buf_string_match(const struct buffer *src, const void *match, int size) |
6fbf66fa |
{ |
81d882d5 |
if (size != src->len)
{
return false;
}
return memcmp(BPTR(src), match, size) == 0; |
6fbf66fa |
}
|
11d21349 |
/**
* Compare first size bytes of src buffer contents with match.
* *NOT* constant time. Do not use when comparing HMACs.
*/ |
6fbf66fa |
static inline bool |
81d882d5 |
buf_string_match_head(const struct buffer *src, const void *match, int size) |
6fbf66fa |
{ |
81d882d5 |
if (size < 0 || size > src->len)
{
return false;
}
return memcmp(BPTR(src), match, size) == 0; |
6fbf66fa |
}
|
81d882d5 |
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); |
6fbf66fa |
/* |
f214bb21 |
* Print a string which might be NULL |
6fbf66fa |
*/ |
81d882d5 |
const char *np(const char *str); |
6fbf66fa |
/*#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) |
9885f57e |
#define CC_LESS_THAN (1<<26)
#define CC_GREATER_THAN (1<<27)
#define CC_PIPE (1<<28)
#define CC_QUESTION_MARK (1<<29)
#define CC_ASTERISK (1<<30) |
6fbf66fa |
/* macro classes */
#define CC_NAME (CC_ALNUM|CC_UNDERBAR)
#define CC_CRLF (CC_CR|CC_NEWLINE)
|
81d882d5 |
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); |
6fbf66fa |
|
81d882d5 |
bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace); |
6fbf66fa |
|
81d882d5 |
const char *string_mod_const(const char *str,
const unsigned int inclusive,
const unsigned int exclusive,
const char replace,
struct gc_arena *gc);
void string_replace_leading(char *str, const char match, const char replace); |
76218836 |
|
ec4dff3b |
/** Return true iff str starts with prefix */
static inline bool
strprefix(const char *str, const char *prefix)
{
return 0 == strncmp(str, prefix, strlen(prefix));
}
|
6fbf66fa |
#ifdef CHARACTER_CLASS_DEBUG |
81d882d5 |
void character_class_debug(void);
|
6fbf66fa |
#endif
/*
* Verify that a pointer is correctly aligned
*/
#ifdef VERIFY_ALIGNMENT |
81d882d5 |
void valign4(const struct buffer *buf, const char *file, const int line);
#define verify_align_4(ptr) valign4(buf, __FILE__, __LINE__) |
6fbf66fa |
#else |
81d882d5 |
#define verify_align_4(ptr) |
6fbf66fa |
#endif
/*
* Very basic garbage collection, mostly for routines that return
* char ptrs to malloced strings.
*/
|
81d882d5 |
void gc_transfer(struct gc_arena *dest, struct gc_arena *src);
void x_gc_free(struct gc_arena *a); |
4e9a51d7 |
|
81d882d5 |
void x_gc_freespecial(struct gc_arena *a); |
6fbf66fa |
|
3cf6c932 |
static inline bool |
81d882d5 |
gc_defined(struct gc_arena *a) |
3cf6c932 |
{ |
81d882d5 |
return a->list != NULL; |
3cf6c932 |
}
|
6fbf66fa |
static inline void |
81d882d5 |
gc_init(struct gc_arena *a) |
6fbf66fa |
{ |
81d882d5 |
a->list = NULL;
a->list_special = NULL; |
6fbf66fa |
}
static inline void |
81d882d5 |
gc_detach(struct gc_arena *a) |
6fbf66fa |
{ |
81d882d5 |
gc_init(a); |
6fbf66fa |
}
static inline struct gc_arena |
81d882d5 |
gc_new(void) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena ret;
gc_init(&ret);
return ret; |
6fbf66fa |
}
static inline void |
81d882d5 |
gc_free(struct gc_arena *a) |
6fbf66fa |
{ |
81d882d5 |
if (a->list)
{
x_gc_free(a);
}
if (a->list_special)
{
x_gc_freespecial(a);
} |
6fbf66fa |
}
static inline void |
81d882d5 |
gc_reset(struct gc_arena *a) |
6fbf66fa |
{ |
81d882d5 |
gc_free(a); |
6fbf66fa |
}
/*
* Allocate memory to hold a structure
*/
#define ALLOC_OBJ(dptr, type) \ |
81d882d5 |
{ \
check_malloc_return((dptr) = (type *) malloc(sizeof(type))); \
} |
6fbf66fa |
#define ALLOC_OBJ_CLEAR(dptr, type) \ |
81d882d5 |
{ \
ALLOC_OBJ(dptr, type); \
memset((dptr), 0, sizeof(type)); \
} |
6fbf66fa |
#define ALLOC_ARRAY(dptr, type, n) \ |
81d882d5 |
{ \
check_malloc_return((dptr) = (type *) malloc(array_mult_safe(sizeof(type), (n), 0))); \
} |
6fbf66fa |
#define ALLOC_ARRAY_GC(dptr, type, n, gc) \ |
81d882d5 |
{ \
(dptr) = (type *) gc_malloc(array_mult_safe(sizeof(type), (n), 0), false, (gc)); \
} |
6fbf66fa |
#define ALLOC_ARRAY_CLEAR(dptr, type, n) \ |
81d882d5 |
{ \
ALLOC_ARRAY(dptr, type, n); \
memset((dptr), 0, (array_mult_safe(sizeof(type), (n), 0))); \
} |
6fbf66fa |
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \ |
81d882d5 |
{ \
(dptr) = (type *) gc_malloc(array_mult_safe(sizeof(type), (n), 0), true, (gc)); \
} |
673f583f |
|
81d882d5 |
#define ALLOC_VAR_ARRAY_CLEAR_GC(dptr, type, atype, n, gc) \
{ \
(dptr) = (type *) gc_malloc(array_mult_safe(sizeof(atype), (n), sizeof(type)), true, (gc)); \
} |
6fbf66fa |
#define ALLOC_OBJ_GC(dptr, type, gc) \ |
81d882d5 |
{ \
(dptr) = (type *) gc_malloc(sizeof(type), false, (gc)); \
} |
6fbf66fa |
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc) \ |
81d882d5 |
{ \
(dptr) = (type *) gc_malloc(sizeof(type), true, (gc)); \
} |
6fbf66fa |
static inline void |
81d882d5 |
check_malloc_return(const void *p) |
6fbf66fa |
{ |
81d882d5 |
if (!p)
{
out_of_memory();
} |
6fbf66fa |
}
|
90efcacb |
/*
* Manage lists of buffers
*/
struct buffer_entry
{ |
81d882d5 |
struct buffer buf;
struct buffer_entry *next; |
90efcacb |
};
struct buffer_list
{ |
81d882d5 |
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 */ |
90efcacb |
};
|
81d882d5 |
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);
struct buffer_entry *buffer_list_push_data(struct buffer_list *ol, const uint8_t *data, size_t size);
struct buffer *buffer_list_peek(struct buffer_list *ol);
void buffer_list_advance(struct buffer_list *ol, int n);
void buffer_list_pop(struct buffer_list *ol); |
90efcacb |
|
81d882d5 |
void buffer_list_aggregate(struct buffer_list *bl, const size_t max); |
90efcacb |
|
fb6138dd |
void buffer_list_aggregate_separator(struct buffer_list *bl,
const size_t max_len, const char *sep); |
90efcacb |
|
81d882d5 |
struct buffer_list *buffer_list_file(const char *fn, int max_line_len); |
7e1c085d |
|
6fbf66fa |
#endif /* BUFFER_H */ |