/* * 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-2009 OpenVPN Technologies, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef BUFFER_H #define BUFFER_H #include "basic.h" #include "thread.h" #define 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 /* basic buffer class for OpenVPN */ struct buffer { int capacity; /* size of buffer allocated by malloc */ int offset; /* data starts at data + offset, offset > 0 to allow for efficient prepending */ int len; /* length of data that starts at data + offset */ uint8_t *data; #ifdef BUF_INIT_TRACKING const char *debug_file; int debug_line; #endif }; /* for garbage collection */ struct gc_entry { struct gc_entry *next; }; struct gc_arena { struct gc_entry *list; }; #define BPTR(buf) (buf_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); struct buffer clear_buf (void); void free_buf (struct buffer *buf); bool buf_assign (struct buffer *dest, const struct buffer *src); void string_clear (char *str); int string_array_len (const char **array); 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 struct buffer alloc_buf (size_t size); struct buffer alloc_buf_gc (size_t size, struct gc_arena *gc); /* allocate buffer with garbage collection */ struct buffer clone_buf (const struct buffer* buf); void *gc_malloc (size_t size, bool clear, struct gc_arena *a); char *string_alloc (const char *str, struct gc_arena *gc); struct buffer string_alloc_buf (const char *str, struct gc_arena *gc); #endif #ifdef BUF_INIT_TRACKING #define buf_init(buf, offset) buf_init_debug (buf, offset, __FILE__, __LINE__) bool buf_init_debug (struct buffer *buf, int offset, const char *file, int line); #else #define buf_init(buf, offset) buf_init_dowork (buf, offset) #endif /* inline functions */ static inline 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) { strncpy (dest, src, maxlen); if (maxlen > 0) dest[maxlen - 1] = 0; } /* return true if string contains at least one numerical digit */ static inline bool has_digit (const unsigned char* src) { unsigned char c; while ((c = *src++)) { if (isdigit(c)) return true; } return false; } /* * printf append to a buffer with overflow check */ bool buf_printf (struct buffer *buf, const char *format, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif ; /* * Like snprintf but guarantees null termination for size > 0 */ int openvpn_snprintf(char *str, size_t size, const char *format, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))) #endif ; /* * remove/add trailing characters */ void buf_null_terminate (struct buffer *buf); void buf_chomp (struct buffer *buf); void buf_rmtail (struct buffer *buf, uint8_t remove); /* * non-buffer string functions */ void chomp (char *str); void rm_trailing_chars (char *str, const char *what_to_delete); const char *skip_leading_whitespace (const char *str); void string_null_terminate (char *str, int len, int capacity); /* * Write string in buf to file descriptor fd. * NOTE: requires that string be null terminated. */ void buf_write_string_file (const struct buffer *buf, const char *filename, int fd); /* * write a string to the end of a buffer that was * truncated by buf_printf */ void buf_catrunc (struct buffer *buf, const char *str); /* * convert a multi-line output to one line */ void convert_to_one_line (struct buffer *buf); /* * Parse a string based on a given delimiter char */ bool buf_parse (struct buffer *buf, const int delim, char *line, const int size); /* * Hex dump -- Output a binary buffer to a hex string and return it. */ char * format_hex_ex (const uint8_t *data, int size, int maxoutput, int space_break, const char* separator, struct gc_arena *gc); static inline char * format_hex (const uint8_t *data, int size, int maxoutput, struct gc_arena *gc) { return format_hex_ex (data, size, maxoutput, 4, " ", gc); } /* * Return a buffer that is a subset of another buffer. */ struct buffer buf_sub (struct buffer *buf, int size, bool prepend); /* * Check if sufficient space to append to buffer. */ static inline bool buf_safe (const struct buffer *buf, int len) { return 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); } } static inline bool buf_string_match (const struct buffer *src, const void *match, int size) { if (size != src->len) return false; return memcmp (BPTR (src), match, size) == 0; } static inline bool buf_string_match_head (const struct buffer *src, const void *match, int size) { if (size < 0 || size > src->len) return false; return memcmp (BPTR (src), match, size) == 0; } bool buf_string_match_head_str (const struct buffer *src, const char *match); bool buf_string_compare_advance (struct buffer *src, const char *match); int buf_substring_len (const struct buffer *buf, int delim); /* * Bitwise operations */ static inline void xor (uint8_t *dest, const uint8_t *src, int len) { while (len-- > 0) *dest++ ^= *src++; } /* * Print a string which might be NULL */ const char *np (const char *str); /*#define CHARACTER_CLASS_DEBUG*/ /* character classes */ #define CC_ANY (1<<0) #define CC_NULL (1<<1) #define CC_ALNUM (1<<2) #define CC_ALPHA (1<<3) #define CC_ASCII (1<<4) #define CC_CNTRL (1<<5) #define CC_DIGIT (1<<6) #define CC_PRINT (1<<7) #define CC_PUNCT (1<<8) #define CC_SPACE (1<<9) #define CC_XDIGIT (1<<10) #define CC_BLANK (1<<11) #define CC_NEWLINE (1<<12) #define CC_CR (1<<13) #define CC_BACKSLASH (1<<14) #define CC_UNDERBAR (1<<15) #define CC_DASH (1<<16) #define CC_DOT (1<<17) #define CC_COMMA (1<<18) #define CC_COLON (1<<19) #define CC_SLASH (1<<20) #define CC_SINGLE_QUOTE (1<<21) #define CC_DOUBLE_QUOTE (1<<22) #define CC_REVERSE_QUOTE (1<<23) #define CC_AT (1<<24) #define CC_EQUAL (1<<25) /* macro classes */ #define CC_NAME (CC_ALNUM|CC_UNDERBAR) #define CC_CRLF (CC_CR|CC_NEWLINE) bool char_class (const unsigned char c, const unsigned int flags); bool string_class (const char *str, const unsigned int inclusive, const unsigned int exclusive); bool string_mod (char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace); const char *string_mod_const (const char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace, struct gc_arena *gc); void string_replace_leading (char *str, const char match, const char replace); #ifdef CHARACTER_CLASS_DEBUG void character_class_debug (void); #endif /* * Verify that a pointer is correctly aligned */ #ifdef VERIFY_ALIGNMENT void valign4 (const struct buffer *buf, const char *file, const int line); # define verify_align_4(ptr) valign4(buf, __FILE__, __LINE__) #else # define verify_align_4(ptr) #endif /* * Very basic garbage collection, mostly for routines that return * char ptrs to malloced strings. */ void gc_transfer (struct gc_arena *dest, struct gc_arena *src); void x_gc_free (struct gc_arena *a); static inline void gc_init (struct gc_arena *a) { a->list = NULL; } static inline void gc_detach (struct gc_arena *a) { gc_init (a); } static inline struct gc_arena gc_new (void) { struct gc_arena ret; ret.list = NULL; return ret; } static inline void gc_free (struct gc_arena *a) { if (a->list) x_gc_free (a); } static inline void gc_reset (struct gc_arena *a) { gc_free (a); } /* * Allocate memory to hold a structure */ void out_of_memory (void); #define ALLOC_OBJ(dptr, type) \ { \ check_malloc_return ((dptr) = (type *) malloc (sizeof (type))); \ } #define ALLOC_OBJ_CLEAR(dptr, type) \ { \ ALLOC_OBJ (dptr, type); \ memset ((dptr), 0, sizeof(type)); \ } #define ALLOC_ARRAY(dptr, type, n) \ { \ check_malloc_return ((dptr) = (type *) malloc (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 (void *p) { void out_of_memory (void); if (!p) out_of_memory (); } /* * Manage lists of buffers */ #ifdef ENABLE_BUFFER_LIST struct buffer_entry { struct buffer buf; struct buffer_entry *next; }; struct buffer_list { struct buffer_entry *head; /* next item to pop/peek */ struct buffer_entry *tail; /* last item pushed */ int size; /* current number of entries */ int max_size; /* maximum size list should grow to */ }; struct buffer_list *buffer_list_new (const int max_size); void buffer_list_free (struct buffer_list *ol); bool buffer_list_defined (const struct buffer_list *ol); void buffer_list_reset (struct buffer_list *ol); void buffer_list_push (struct buffer_list *ol, const unsigned char *str); struct buffer *buffer_list_peek (struct buffer_list *ol); void buffer_list_advance (struct buffer_list *ol, int n); void buffer_list_aggregate (struct buffer_list *bl, const size_t max); struct buffer_list *buffer_list_file (const char *fn, int max_line_len); #endif #endif /* BUFFER_H */