| /* |
| * 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-2018 OpenVPN Inc <sales@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; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #ifndef BUFFER_H |
| #define BUFFER_H |
| |
| #include "basic.h" |
| #include "error.h" |
| |
| #define BUF_SIZE_MAX 1000000 |
| |
| /* |
| * 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 |
| |
| /**************************************************************************/ |
| /** |
| * 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: |
| * @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 |
| */ |
| struct buffer |
| { |
| int capacity; /**< Size in bytes of memory allocated by |
| * \c malloc(). */ |
| int offset; /**< Offset in bytes of the actual content |
| * within the allocated memory. */ |
| int len; /**< Length in bytes of the actual content |
| * within the allocated memory. */ |
| uint8_t *data; /**< Pointer to the allocated memory. */ |
| |
| #ifdef BUF_INIT_TRACKING |
| const char *debug_file; |
| int debug_line; |
| #endif |
| }; |
| |
| |
| /**************************************************************************/ |
| /** |
| * 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. |
| */ |
| struct gc_entry |
| { |
| struct gc_entry *next; /**< Pointer to the next item in the |
| * linked list. */ |
| }; |
| |
| /** |
| * Garbage collection entry for a specially allocated structure that needs |
| * a custom free function to be freed like struct addrinfo |
| * |
| */ |
| struct gc_entry_special |
| { |
| struct gc_entry_special *next; |
| void (*free_fnc)(void *); |
| void *addr; |
| }; |
| |
| |
| /** |
| * 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. |
| */ |
| struct gc_arena |
| { |
| struct gc_entry *list; /**< First element of the linked list of |
| * \c gc_entry structures. */ |
| struct gc_entry_special *list_special; |
| }; |
| |
| |
| #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)) |
| #define BCAP(buf) (buf_forward_capacity(buf)) |
| |
| void buf_clear(struct buffer *buf); |
| |
| 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); |
| |
| size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra); |
| |
| #define PA_BRACKET (1<<0) |
| char *print_argv(const char **p, struct gc_arena *gc, const unsigned int flags); |
| |
| void buf_size_error(const size_t size); |
| |
| /* 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 /* 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 */ |
| |
| 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); |
| |
| |
| #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 */ |
| inline static void |
| gc_freeaddrinfo_callback(void *addr) |
| { |
| freeaddrinfo((struct addrinfo *) addr); |
| } |
| |
| /** Return an empty struct buffer */ |
| static inline struct buffer |
| clear_buf(void) |
| { |
| return (struct buffer) { 0 }; |
| } |
| |
| static inline bool |
| buf_defined(const struct buffer *buf) |
| { |
| return buf->data != NULL; |
| } |
| |
| static inline bool |
| buf_valid(const struct buffer *buf) |
| { |
| return likely(buf->data != NULL) && likely(buf->len >= 0); |
| } |
| |
| static inline uint8_t * |
| buf_bptr(const struct buffer *buf) |
| { |
| if (buf_valid(buf)) |
| { |
| return buf->data + buf->offset; |
| } |
| else |
| { |
| return NULL; |
| } |
| } |
| |
| static int |
| buf_len(const struct buffer *buf) |
| { |
| if (buf_valid(buf)) |
| { |
| return buf->len; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static inline uint8_t * |
| buf_bend(const struct buffer *buf) |
| { |
| return buf_bptr(buf) + buf_len(buf); |
| } |
| |
| static inline uint8_t * |
| buf_blast(const struct buffer *buf) |
| { |
| if (buf_len(buf) > 0) |
| { |
| return buf_bptr(buf) + buf_len(buf) - 1; |
| } |
| else |
| { |
| return NULL; |
| } |
| } |
| |
| static inline bool |
| buf_size_valid(const size_t size) |
| { |
| return likely(size < BUF_SIZE_MAX); |
| } |
| |
| static inline bool |
| buf_size_valid_signed(const int size) |
| { |
| return likely(size >= -BUF_SIZE_MAX) && likely(size < BUF_SIZE_MAX); |
| } |
| |
| static inline char * |
| buf_str(const struct buffer *buf) |
| { |
| return (char *)buf_bptr(buf); |
| } |
| |
| 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 void |
| buf_set_write(struct buffer *buf, uint8_t *data, int size) |
| { |
| 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; |
| } |
| } |
| |
| static inline void |
| buf_set_read(struct buffer *buf, const uint8_t *data, int size) |
| { |
| if (!buf_size_valid(size)) |
| { |
| buf_size_error(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) |
| { |
| if (maxlen > 0) |
| { |
| strncpy(dest, src, maxlen-1); |
| 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; |
| } |
| |
| /** |
| * 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. |
| * |
| * @param data Pointer to data to zeroise. |
| * @param len Length of data, in bytes. |
| */ |
| static inline void |
| secure_memzero(void *data, size_t len) |
| { |
| #if defined(_WIN32) |
| SecureZeroMemory(data, len); |
| #elif defined(__GNUC__) || defined(__clang__) |
| memset(data, 0, len); |
| __asm__ __volatile__ ("" : : "r" (data) : "memory"); |
| #else |
| volatile char *p = (volatile char *) data; |
| while (len--) |
| { |
| *p++ = 0; |
| } |
| #endif |
| } |
| |
| /* |
| * 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 |
| */ |
| bool buf_printf(struct buffer *buf, const char *format, ...) |
| #ifdef __GNUC__ |
| #if __USE_MINGW_ANSI_STDIO |
| __attribute__ ((format(gnu_printf, 2, 3))) |
| #else |
| __attribute__ ((format(__printf__, 2, 3))) |
| #endif |
| #endif |
| ; |
| |
| /* |
| * puts append to a buffer with overflow check |
| */ |
| bool buf_puts(struct buffer *buf, const char *str); |
| |
| /* |
| * Like snprintf but guarantees null termination for size > 0 |
| */ |
| bool openvpn_snprintf(char *str, size_t size, const char *format, ...) |
| #ifdef __GNUC__ |
| #if __USE_MINGW_ANSI_STDIO |
| __attribute__ ((format(gnu_printf, 3, 4))) |
| #else |
| __attribute__ ((format(__printf__, 3, 4))) |
| #endif |
| #endif |
| ; |
| |
| |
| #ifdef _WIN32 |
| /* |
| * Like swprintf but guarantees null termination for size > 0 |
| * |
| * This is under #ifdef because only Windows-specific code in tun.c |
| * uses this function and its implementation breaks OpenBSD <= 4.9 |
| */ |
| bool |
| openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...); |
| |
| /* |
| * Unlike in openvpn_snprintf, we cannot use format attributes since |
| * GCC doesn't support wprintf as archetype. |
| */ |
| #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 buffer contents to file. |
| * |
| * @param filename The filename to write the buffer to. |
| * @param buf The buffer to write to the file. |
| * |
| * @return true on success, false otherwise. |
| */ |
| bool buffer_write_file(const char *filename, const struct buffer *buf); |
| |
| /* |
| * 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. |
| */ |
| #define FHE_SPACE_BREAK_MASK 0xFF /* space_break parameter in lower 8 bits */ |
| #define FHE_CAPS 0x100 /* output hex in caps */ |
| char * |
| format_hex_ex(const uint8_t *data, int size, int maxoutput, |
| unsigned int space_break_flags, 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 buf_valid(buf) && buf_size_valid(len) |
| && buf->offset + buf->len + len <= buf->capacity; |
| } |
| |
| static inline bool |
| buf_safe_bidir(const struct buffer *buf, int len) |
| { |
| if (buf_valid(buf) && buf_size_valid_signed(len)) |
| { |
| const int newlen = buf->len + len; |
| return newlen >= 0 && buf->offset + newlen <= buf->capacity; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| static inline int |
| buf_forward_capacity(const struct buffer *buf) |
| { |
| if (buf_valid(buf)) |
| { |
| int ret = buf->capacity - (buf->offset + buf->len); |
| if (ret < 0) |
| { |
| ret = 0; |
| } |
| return ret; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static inline int |
| buf_forward_capacity_total(const struct buffer *buf) |
| { |
| if (buf_valid(buf)) |
| { |
| int ret = buf->capacity - buf->offset; |
| if (ret < 0) |
| { |
| ret = 0; |
| } |
| return ret; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| static inline int |
| buf_reverse_capacity(const struct buffer *buf) |
| { |
| if (buf_valid(buf)) |
| { |
| return buf->offset; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| 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 (!buf_valid(buf) || 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 (!buf_valid(buf) || 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); |
| } |
| } |
| |
| /** Return true if buffer contents are equal */ |
| static inline bool |
| buf_equal(const struct buffer *a, const struct buffer *b) |
| { |
| return BLEN(a) == BLEN(b) && 0 == memcmp(BPTR(a), BPTR(b), BLEN(a)); |
| } |
| |
| /** |
| * Compare src buffer contents with match. |
| * *NOT* constant time. Do not use when comparing HMACs. |
| */ |
| 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; |
| } |
| |
| /** |
| * Compare first size bytes of src buffer contents with match. |
| * *NOT* constant time. Do not use when comparing HMACs. |
| */ |
| 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); |
| |
| /* |
| * 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) |
| #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) |
| |
| /* 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); |
| |
| void string_replace_leading(char *str, const char match, const char replace); |
| |
| /** Return true iff str starts with prefix */ |
| static inline bool |
| strprefix(const char *str, const char *prefix) |
| { |
| return 0 == strncmp(str, prefix, strlen(prefix)); |
| } |
| |
| |
| #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); |
| |
| void x_gc_freespecial(struct gc_arena *a); |
| |
| static inline bool |
| gc_defined(struct gc_arena *a) |
| { |
| return a->list != NULL; |
| } |
| |
| static inline void |
| gc_init(struct gc_arena *a) |
| { |
| a->list = NULL; |
| a->list_special = 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; |
| gc_init(&ret); |
| return ret; |
| } |
| |
| static inline void |
| gc_free(struct gc_arena *a) |
| { |
| if (a->list) |
| { |
| x_gc_free(a); |
| } |
| if (a->list_special) |
| { |
| x_gc_freespecial(a); |
| } |
| } |
| |
| static inline void |
| gc_reset(struct gc_arena *a) |
| { |
| gc_free(a); |
| } |
| |
| /* |
| * Allocate memory to hold a structure |
| */ |
| |
| #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(array_mult_safe(sizeof(type), (n), 0))); \ |
| } |
| |
| #define ALLOC_ARRAY_GC(dptr, type, n, gc) \ |
| { \ |
| (dptr) = (type *) gc_malloc(array_mult_safe(sizeof(type), (n), 0), false, (gc)); \ |
| } |
| |
| #define ALLOC_ARRAY_CLEAR(dptr, type, n) \ |
| { \ |
| ALLOC_ARRAY(dptr, type, n); \ |
| memset((dptr), 0, (array_mult_safe(sizeof(type), (n), 0))); \ |
| } |
| |
| #define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \ |
| { \ |
| (dptr) = (type *) gc_malloc(array_mult_safe(sizeof(type), (n), 0), true, (gc)); \ |
| } |
| |
| #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)); \ |
| } |
| |
| #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(const void *p) |
| { |
| if (!p) |
| { |
| out_of_memory(); |
| } |
| } |
| |
| /* |
| * Manage lists of buffers |
| */ |
| 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 */ |
| }; |
| |
| /** |
| * Allocate an empty buffer list of capacity \c max_size. |
| * |
| * @param max_size the capacity of the list to allocate |
| * |
| * @return the new list |
| */ |
| struct buffer_list *buffer_list_new(const int max_size); |
| |
| /** |
| * Frees a buffer list and all the buffers in it. |
| * |
| * @param ol the list to free |
| */ |
| void buffer_list_free(struct buffer_list *ol); |
| |
| /** |
| * Checks if the list is valid and non-empty |
| * |
| * @param ol the list to check |
| * |
| * @return true iff \c ol is not NULL and contains at least one buffer |
| */ |
| bool buffer_list_defined(const struct buffer_list *ol); |
| |
| /** |
| * Empty the list \c ol and frees all the contained buffers |
| * |
| * @param ol the list to reset |
| */ |
| void buffer_list_reset(struct buffer_list *ol); |
| |
| /** |
| * Allocates and appends a new buffer containing \c str as data to \c ol |
| * |
| * @param ol the list to append the new buffer to |
| * @param str the string to copy into the new buffer |
| */ |
| void buffer_list_push(struct buffer_list *ol, const char *str); |
| |
| /** |
| * Allocates and appends a new buffer containing \c data of length \c size. |
| * |
| * @param ol the list to append the new buffer to |
| * @param data the data to copy into the new buffer |
| * @param size the length of \c data to copy into the buffer |
| * |
| * @return the new buffer |
| */ |
| struct buffer_entry *buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size); |
| |
| /** |
| * Retrieve the head buffer |
| * |
| * @param ol the list to retrieve the buffer from |
| * |
| * @return a pointer to the head buffer or NULL if the list is empty |
| */ |
| 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); |
| |
| /** |
| * Aggregates as many buffers as possible from \c bl in a new buffer of maximum |
| * length \c max_len . |
| * All the aggregated buffers are removed from the list and replaced by the new |
| * one, followed by any additional (non-aggregated) data. |
| * |
| * @param bl the list of buffer to aggregate |
| * @param max the maximum length of the aggregated buffer |
| */ |
| void buffer_list_aggregate(struct buffer_list *bl, const size_t max); |
| |
| /** |
| * Aggregates as many buffers as possible from \c bl in a new buffer |
| * of maximum length \c max_len . \c sep is written after |
| * each copied buffer (also after the last one). All the aggregated buffers are |
| * removed from the list and replaced by the new one, followed by any additional |
| * (non-aggregated) data. |
| * Nothing happens if \c max_len is not enough to aggregate at least 2 buffers. |
| * |
| * @param bl the list of buffer to aggregate |
| * @param max_len the maximum length of the aggregated buffer |
| * @param sep the separator to put between buffers during aggregation |
| */ |
| void buffer_list_aggregate_separator(struct buffer_list *bl, |
| const size_t max_len, const char *sep); |
| |
| struct buffer_list *buffer_list_file(const char *fn, int max_line_len); |
| |
| /** |
| * buffer_read_from_file - copy the content of a file into a buffer |
| * |
| * @param file path to the file to read |
| * @param gc the garbage collector to use when allocating the buffer. It |
| * is passed to alloc_buf_gc() and therefore can be NULL. |
| * |
| * @return the buffer storing the file content or an invalid buffer in case of |
| * error |
| */ |
| struct buffer buffer_read_from_file(const char *filename, struct gc_arena *gc); |
| |
| #endif /* BUFFER_H */ |