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.
  *
d7fa38f2
  *  Copyright (C) 2002-2009 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.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #ifndef BUFFER_H
 #define BUFFER_H
 
 #include "basic.h"
 #include "thread.h"
 
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
 
 /* basic buffer class for OpenVPN */
 
 struct buffer
 {
   int capacity;	   /* size of buffer allocated by malloc */
   int offset;	   /* data starts at data + offset, offset > 0 to allow for efficient prepending */
   int len;	   /* length of data that starts at data + offset */
   uint8_t *data;
 
 #ifdef BUF_INIT_TRACKING
   const char *debug_file;
   int debug_line;
 #endif
 };
 
 /* for garbage collection */
 
 struct gc_entry
 {
   struct gc_entry *next;
 };
 
 struct gc_arena
 {
   struct gc_entry *list;
 };
 
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))
6fbf66fa
 #define BCAP(buf)  (buf_forward_capacity (buf))
 
 void buf_clear (struct buffer *buf);
 
 struct buffer clear_buf (void);
 void free_buf (struct buffer *buf);
 
 bool buf_assign (struct buffer *dest, const struct buffer *src);
 
d40f2b20
 void string_clear (char *str);
eadf16a6
 int string_array_len (const char **array);
 
8e986316
 size_t array_mult_safe (const size_t m1, const size_t m2);
 
eadf16a6
 #define PA_BRACKET (1<<0)
 char *print_argv (const char **p, struct gc_arena *gc, const unsigned int flags);
6fbf66fa
 
b551bec9
 void buf_size_error (const size_t size);
 
6fbf66fa
 /* for dmalloc debugging */
 
 #ifdef DMALLOC
 
 #define alloc_buf(size)               alloc_buf_debug (size, __FILE__, __LINE__)
 #define alloc_buf_gc(size, gc)        alloc_buf_gc_debug (size, gc, __FILE__, __LINE__);
 #define clone_buf(buf)                clone_buf_debug (buf, __FILE__, __LINE__);
 #define gc_malloc(size, clear, arena) gc_malloc_debug (size, clear, arena, __FILE__, __LINE__)
 #define string_alloc(str, gc)         string_alloc_debug (str, gc, __FILE__, __LINE__)
 #define string_alloc_buf(str, gc)     string_alloc_buf_debug (str, gc, __FILE__, __LINE__)
 
 struct buffer alloc_buf_debug (size_t size, const char *file, int line);
 struct buffer alloc_buf_gc_debug (size_t size, struct gc_arena *gc, const char *file, int line);
 struct buffer clone_buf_debug (const struct buffer* buf, const char *file, int line);
 void *gc_malloc_debug (size_t size, bool clear, struct gc_arena *a, const char *file, int line);
 char *string_alloc_debug (const char *str, struct gc_arena *gc, const char *file, int line);
 struct buffer string_alloc_buf_debug (const char *str, struct gc_arena *gc, const char *file, int line);
 
 #else
 
 struct buffer alloc_buf (size_t size);
 struct buffer alloc_buf_gc (size_t size, struct gc_arena *gc); /* allocate buffer with garbage collection */
 struct buffer clone_buf (const struct buffer* buf);
 void *gc_malloc (size_t size, bool clear, struct gc_arena *a);
 char *string_alloc (const char *str, struct gc_arena *gc);
 struct buffer string_alloc_buf (const char *str, struct gc_arena *gc);
 
 #endif
 
 #ifdef BUF_INIT_TRACKING
 #define buf_init(buf, offset) buf_init_debug (buf, offset, __FILE__, __LINE__)
 bool buf_init_debug (struct buffer *buf, int offset, const char *file, int line);
 #else
 #define buf_init(buf, offset) buf_init_dowork (buf, offset)
 #endif
 
 
 /* inline functions */
 
b551bec9
 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);
 }
 
6fbf66fa
 static inline void
 buf_reset (struct buffer *buf)
 {
   buf->capacity = 0;
   buf->offset = 0;
   buf->len = 0;
   buf->data = NULL;
 }
 
47ae8457
 static inline void
 buf_reset_len (struct buffer *buf)
 {
   buf->len = 0;
   buf->offset = 0;
 }
 
6fbf66fa
 static inline bool
 buf_init_dowork (struct buffer *buf, int offset)
 {
cc1d3998
   if (offset < 0 || offset > buf->capacity || buf->data == NULL)
6fbf66fa
     return false;
   buf->len = 0;
   buf->offset = offset;
   return true;
 }
 
 static inline void
 buf_set_write (struct buffer *buf, uint8_t *data, int size)
 {
b551bec9
   if (!buf_size_valid (size))
     buf_size_error (size);
6fbf66fa
   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)
 {
b551bec9
   if (!buf_size_valid (size))
     buf_size_error (size);
6fbf66fa
   buf->len = buf->capacity = size;
   buf->offset = 0;
   buf->data = (uint8_t *)data;
 }
 
 /* Like strncpy but makes sure dest is always null terminated */
 static inline void
 strncpynt (char *dest, const char *src, size_t maxlen)
 {
   strncpy (dest, src, maxlen);
   if (maxlen > 0)
     dest[maxlen - 1] = 0;
 }
 
 /* return true if string contains at least one numerical digit */
 static inline bool
dcc0b244
 has_digit (const unsigned char* src)
6fbf66fa
 {
dcc0b244
   unsigned char c;
6fbf66fa
   while ((c = *src++))
     {
       if (isdigit(c))
 	return true;
     }
   return false;
 }
 
 /*
  * printf append to a buffer with overflow check
  */
bda8d38b
 bool buf_printf (struct buffer *buf, const char *format, ...)
6fbf66fa
 #ifdef __GNUC__
     __attribute__ ((format (printf, 2, 3)))
 #endif
     ;
 
 /*
  * Like snprintf but guarantees null termination for size > 0
  */
 int openvpn_snprintf(char *str, size_t size, const char *format, ...)
 #ifdef __GNUC__
     __attribute__ ((format (printf, 3, 4)))
 #endif
     ;
 
 /*
  * remove/add trailing characters
  */
 
 void buf_null_terminate (struct buffer *buf);
 void buf_chomp (struct buffer *buf);
 void buf_rmtail (struct buffer *buf, uint8_t remove);
 
 /*
  * non-buffer string functions
  */
 void chomp (char *str);
47ae8457
 void rm_trailing_chars (char *str, const char *what_to_delete);
eadf16a6
 const char *skip_leading_whitespace (const char *str);
6fbf66fa
 void string_null_terminate (char *str, int len, int capacity);
 
 /*
  * Write string in buf to file descriptor fd.
  * NOTE: requires that string be null terminated.
  */
 void buf_write_string_file (const struct buffer *buf, const char *filename, int fd);
 
 /*
  * write a string to the end of a buffer that was
  * truncated by buf_printf
  */
 void buf_catrunc (struct buffer *buf, const char *str);
 
 /*
  * convert a multi-line output to one line
  */
 void convert_to_one_line (struct buffer *buf);
 
 /*
  * Parse a string based on a given delimiter char
  */
 bool buf_parse (struct buffer *buf, const int delim, char *line, const int size);
 
 /*
  * Hex dump -- Output a binary buffer to a hex string and return it.
  */
 char *
 format_hex_ex (const uint8_t *data, int size, int maxoutput,
 	       int space_break, const char* separator,
 	       struct gc_arena *gc);
 
 static inline char *
 format_hex (const uint8_t *data, int size, int maxoutput, struct gc_arena *gc)
 {
   return format_hex_ex (data, size, maxoutput, 4, " ", gc);
 }
 
 /*
  * Return a buffer that is a subset of another buffer.
  */
 struct buffer buf_sub (struct buffer *buf, int size, bool prepend);
 
 /*
  * Check if sufficient space to append to buffer.
  */
 
 static inline bool
 buf_safe (const struct buffer *buf, int len)
 {
b551bec9
   return buf_valid (buf) && buf_size_valid (len)
     && buf->offset + buf->len + len <= buf->capacity;
6fbf66fa
 }
 
 static inline bool
 buf_safe_bidir (const struct buffer *buf, int len)
 {
b551bec9
   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;
6fbf66fa
 }
 
 static inline int
 buf_forward_capacity (const struct buffer *buf)
 {
b551bec9
   if (buf_valid (buf))
     {
       int ret = buf->capacity - (buf->offset + buf->len);
       if (ret < 0)
 	ret = 0;
       return ret;
     }
   else
     return 0;
6fbf66fa
 }
 
 static inline int
 buf_forward_capacity_total (const struct buffer *buf)
 {
b551bec9
   if (buf_valid (buf))
     {
       int ret = buf->capacity - buf->offset;
       if (ret < 0)
 	ret = 0;
       return ret;
     }
   else
     return 0;
6fbf66fa
 }
 
 static inline int
 buf_reverse_capacity (const struct buffer *buf)
 {
b551bec9
   if (buf_valid (buf))
     return buf->offset;
   else
     return 0;
6fbf66fa
 }
 
 static inline bool
 buf_inc_len (struct buffer *buf, int inc)
 {
cc1d3998
   if (!buf_safe_bidir (buf, inc))
6fbf66fa
     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)
 {
b551bec9
   if (!buf_valid (buf) || size < 0 || size > buf->offset)
6fbf66fa
     return NULL;
   buf->offset -= size;
   buf->len += size;
   return BPTR (buf);
 }
 
 static inline bool
 buf_advance (struct buffer *buf, int size)
 {
b551bec9
   if (!buf_valid (buf) || size < 0 || buf->len < size)
6fbf66fa
     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)
 {
cc1d3998
   if (src_index < 0
       || src_len < 0
       || src_index + src_len > src->len
       || dest_index < 0
       || dest->offset + dest_index + src_len > dest->capacity)
6fbf66fa
     return false;
   memcpy (dest->data + dest->offset + dest_index, src->data + src->offset + src_index, src_len);
   if (dest_index + src_len > dest->len)
     dest->len = dest_index + src_len;
   return true;
 }
 
 /* truncate src to len, copy excess data beyond len to dest */
 static inline bool
 buf_copy_excess (struct buffer *dest,
 		 struct buffer *src,
 		 int len)
 {
   if (len < 0)
     return false;
   if (src->len > len)
     {
       struct buffer b = *src;
       src->len = len;
       if (!buf_advance (&b, len))
 	return false;
       return buf_copy (dest, &b);
     }
   else
     {
       return true;
     }
 }
 
 static inline bool
 buf_read (struct buffer *src, void *dest, int size)
 {
   uint8_t *cp = buf_read_alloc (src, size);
   if (!cp)
     return false;
   memcpy (dest, cp, size);
   return true;
 }
 
 static inline int
 buf_read_u8 (struct buffer *buf)
 {
   int ret;
   if (BLEN (buf) < 1)
     return -1;
   ret = *BPTR(buf);
   buf_advance (buf, 1);
   return ret;
 }
 
 static inline int
 buf_read_u16 (struct buffer *buf)
 {
   uint16_t ret;
   if (!buf_read (buf, &ret, sizeof (uint16_t)))
     return -1;
   return ntohs (ret);
 }
 
 static inline uint32_t
 buf_read_u32 (struct buffer *buf, bool *good)
 {
   uint32_t ret;
   if (!buf_read (buf, &ret, sizeof (uint32_t)))
     {
       if (good)
 	*good = false;
       return 0;
     }
   else
     {
       if (good)
 	*good = true;
       return ntohl (ret);
     }
 }
 
 static inline bool
 buf_string_match (const struct buffer *src, const void *match, int size)
 {
   if (size != src->len)
     return false;
   return memcmp (BPTR (src), match, size) == 0;
 }
 
 static inline bool
 buf_string_match_head (const struct buffer *src, const void *match, int size)
 {
   if (size < 0 || size > src->len)
     return false;
   return memcmp (BPTR (src), match, size) == 0;
 }
 
 bool buf_string_match_head_str (const struct buffer *src, const char *match);
 bool buf_string_compare_advance (struct buffer *src, const char *match);
 int buf_substring_len (const struct buffer *buf, int delim);
 
 /*
  * Bitwise operations
  */
 static inline void
 xor (uint8_t *dest, const uint8_t *src, int len)
 {
   while (len-- > 0)
     *dest++ ^= *src++;
 }
 
 /*
f214bb21
  * Print a string which might be NULL
6fbf66fa
  */
f214bb21
 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)
 
 /* macro classes */
 #define CC_NAME               (CC_ALNUM|CC_UNDERBAR)
 #define CC_CRLF               (CC_CR|CC_NEWLINE)
 
dcc0b244
 bool char_class (const unsigned char c, const unsigned int flags);
6fbf66fa
 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);
 
76218836
 void string_replace_leading (char *str, const char match, const char replace);
 
6fbf66fa
 #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.
  */
 
4e9a51d7
 void gc_transfer (struct gc_arena *dest, struct gc_arena *src);
 
6fbf66fa
 void x_gc_free (struct gc_arena *a);
 
 static inline void
 gc_init (struct gc_arena *a)
 {
   a->list = NULL;
 }
 
 static inline void
 gc_detach (struct gc_arena *a)
 {
   gc_init (a);
 }
 
 static inline struct gc_arena
 gc_new (void)
 {
   struct gc_arena ret;
   ret.list = NULL;
   return ret;
 }
 
 static inline void
 gc_free (struct gc_arena *a)
 {
   if (a->list)
     x_gc_free (a);
 }
 
 static inline void
 gc_reset (struct gc_arena *a)
 {
   gc_free (a);
 }
 
 /*
  * Allocate memory to hold a structure
  */
 
 void out_of_memory (void);
 
 #define ALLOC_OBJ(dptr, type) \
 { \
   check_malloc_return ((dptr) = (type *) malloc (sizeof (type))); \
 }
 
 #define ALLOC_OBJ_CLEAR(dptr, type) \
 { \
   ALLOC_OBJ (dptr, type); \
   memset ((dptr), 0, sizeof(type)); \
 }
 
 #define ALLOC_ARRAY(dptr, type, n) \
 { \
8e986316
   check_malloc_return ((dptr) = (type *) malloc (array_mult_safe (sizeof (type), (n)))); \
6fbf66fa
 }
 
 #define ALLOC_ARRAY_GC(dptr, type, n, gc) \
 { \
8e986316
   (dptr) = (type *) gc_malloc (array_mult_safe (sizeof (type), (n)), false, (gc)); \
6fbf66fa
 }
 
 #define ALLOC_ARRAY_CLEAR(dptr, type, n) \
 { \
   ALLOC_ARRAY (dptr, type, n); \
8e986316
   memset ((dptr), 0, (array_mult_safe (sizeof(type), (n)))); \
6fbf66fa
 }
 
 #define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \
 { \
8e986316
   (dptr) = (type *) gc_malloc (array_mult_safe (sizeof (type), (n)), true, (gc)); \
6fbf66fa
 }
 
 #define ALLOC_OBJ_GC(dptr, type, gc) \
 { \
   (dptr) = (type *) gc_malloc (sizeof (type), false, (gc)); \
 }
 
 #define ALLOC_OBJ_CLEAR_GC(dptr, type, gc) \
 { \
   (dptr) = (type *) gc_malloc (sizeof (type), true, (gc)); \
 }
 
 static inline void
 check_malloc_return (void *p)
 {
   void out_of_memory (void);
   if (!p)
     out_of_memory ();
 }
 
90efcacb
 /*
  * Manage lists of buffers
  */
 
 #ifdef ENABLE_BUFFER_LIST
 
 struct buffer_entry
 {
   struct buffer buf;
   struct buffer_entry *next;
 };
 
 struct buffer_list
 {
   struct buffer_entry *head; /* next item to pop/peek */
   struct buffer_entry *tail; /* last item pushed */
   int size;                  /* current number of entries */
   int max_size;              /* maximum size list should grow to */
 };
 
 struct buffer_list *buffer_list_new (const int max_size);
 void buffer_list_free (struct buffer_list *ol);
 
 bool buffer_list_defined (const struct buffer_list *ol);
 void buffer_list_reset (struct buffer_list *ol);
 
 void buffer_list_push (struct buffer_list *ol, const unsigned char *str);
 const struct buffer *buffer_list_peek (struct buffer_list *ol);
 void buffer_list_advance (struct buffer_list *ol, int n);
 
 struct buffer_list *buffer_list_file (const char *fn, int max_line_len);
 
 #endif
 
6fbf66fa
 #endif /* BUFFER_H */