src/openvpn/buffer.h
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.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN 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
 
43a5a4f3
 
4ce1a9b6
 #ifdef _WIN32
43a5a4f3
 /*
  * Like swprintf but guarantees null termination for size > 0
4ce1a9b6
  *
  * This is under #ifdef because only Windows-specific code in tun.c
  * uses this function and its implementation breaks OpenBSD <= 4.9
43a5a4f3
  */
 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.
  */
4ce1a9b6
 #endif
43a5a4f3
 
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
 
a8fa1679
 /**
  * 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.
6fbf66fa
  */
a8fa1679
 bool buffer_write_file(const char *filename, const struct buffer *buf);
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
 };
 
e72b2f2c
 /**
  * Allocate an empty buffer list of capacity \c max_size.
  *
  * @param max_size  the capacity of the list to allocate
  *
  * @return the new list
  */
81d882d5
 struct buffer_list *buffer_list_new(const int max_size);
 
e72b2f2c
 /**
  * Frees a buffer list and all the buffers in it.
  *
  * @param ol    the list to free
  */
81d882d5
 void buffer_list_free(struct buffer_list *ol);
 
e72b2f2c
 /**
  * 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
  */
81d882d5
 bool buffer_list_defined(const struct buffer_list *ol);
 
e72b2f2c
 /**
  * Empty the list \c ol and frees all the contained buffers
  *
  * @param ol    the list to reset
  */
81d882d5
 void buffer_list_reset(struct buffer_list *ol);
 
e72b2f2c
 /**
  * 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
  */
b395f36e
 void buffer_list_push(struct buffer_list *ol, const char *str);
81d882d5
 
e72b2f2c
 /**
  * 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
  */
b395f36e
 struct buffer_entry *buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size);
81d882d5
 
e72b2f2c
 /**
  * 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
  */
81d882d5
 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
 
e72b2f2c
 /**
  * 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
  */
81d882d5
 void buffer_list_aggregate(struct buffer_list *bl, const size_t max);
90efcacb
 
e72b2f2c
 /**
  * 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
  */
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
 
5817b49b
 /**
  * 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);
 
6fbf66fa
 #endif /* BUFFER_H */