OpenVPN
buffer.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "common.h"
30#include "buffer.h"
31#include "error.h"
32#include "mtu.h"
33#include "misc.h"
34
35#include "memdbg.h"
36
37#include <wchar.h>
38
39size_t
40array_mult_safe(const size_t m1, const size_t m2, const size_t extra)
41{
42 const size_t limit = ALLOC_SIZE_MAX;
43 unsigned long long res =
44 (unsigned long long)m1 * (unsigned long long)m2 + (unsigned long long)extra;
45 if (unlikely(m1 > limit) || unlikely(m2 > limit) || unlikely(extra > limit)
46 || unlikely(res > limit))
47 {
48 msg(M_FATAL, "attempted allocation of excessively large array");
49 }
50 return (size_t)res;
51}
52
53void
54buf_size_error(const size_t size)
55{
56 msg(M_FATAL, "fatal buffer size error, size=%zu", size);
57}
58
59struct buffer
61alloc_buf_debug(size_t size, const char *file, int line)
62#else
63alloc_buf(size_t size)
64#endif
65{
66 struct buffer buf;
67 CLEAR(buf);
68
69 if (!buf_size_valid(size))
70 {
71 buf_size_error(size);
72 }
73 buf.capacity = (int)size;
74#ifdef DMALLOC
75 buf.data = openvpn_dmalloc(file, line, size);
76#else
77 buf.data = calloc(1, size);
78#endif
80
81 return buf;
82}
83
84struct buffer
86alloc_buf_gc_debug(size_t size, struct gc_arena *gc, const char *file, int line)
87#else
88alloc_buf_gc(size_t size, struct gc_arena *gc)
89#endif
90{
91 struct buffer buf;
92 CLEAR(buf);
93
94 if (!buf_size_valid(size))
95 {
96 buf_size_error(size);
97 }
98 buf.capacity = (int)size;
99#ifdef DMALLOC
100 buf.data = (uint8_t *)gc_malloc_debug(size, false, gc, file, line);
101#else
102 buf.data = (uint8_t *)gc_malloc(size, false, gc);
103#endif
104 if (size)
105 {
106 *buf.data = 0;
107 }
108 return buf;
109}
110
111struct buffer
113clone_buf_debug(const struct buffer *buf, const char *file, int line)
114#else
115clone_buf(const struct buffer *buf)
116#endif
117{
118 struct buffer ret;
119 ret.capacity = buf->capacity;
120 ret.offset = buf->offset;
121 ret.len = buf->len;
122#ifdef BUF_INIT_TRACKING
123 ret.debug_file = buf->debug_file;
124 ret.debug_line = buf->debug_line;
125#endif
126#ifdef DMALLOC
127 ret.data = (uint8_t *)openvpn_dmalloc(file, line, buf->capacity);
128#else
129 ret.data = (uint8_t *)malloc(buf->capacity);
130#endif
132 memcpy(BPTR(&ret), BPTR(buf), BLENZ(buf));
133 return ret;
134}
135
136#ifdef BUF_INIT_TRACKING
137
138bool
139buf_init_debug(struct buffer *buf, int offset, const char *file, int line)
140{
141 buf->debug_file = file;
142 buf->debug_line = line;
143 return buf_init_dowork(buf, offset);
144}
145
146#ifdef VERIFY_ALIGNMENT
147static inline int
148buf_debug_line(const struct buffer *buf)
149{
150 return buf->debug_line;
151}
152
153static const char *
154buf_debug_file(const struct buffer *buf)
155{
156 return buf->debug_file;
157}
158#endif
159
160#else /* ifdef BUF_INIT_TRACKING */
161
162#define buf_debug_line(buf) 0
163#define buf_debug_file(buf) "[UNDEF]"
164
165#endif /* ifdef BUF_INIT_TRACKING */
166
167void
168buf_clear(struct buffer *buf)
169{
170 if (buf->capacity > 0)
171 {
172 secure_memzero(buf->data, buf->capacity);
173 }
174 buf->len = 0;
175 buf->offset = 0;
176}
177
178bool
179buf_assign(struct buffer *dest, const struct buffer *src)
180{
181 if (!buf_init(dest, src->offset))
182 {
183 return false;
184 }
185 return buf_write(dest, BPTR(src), BLENZ(src));
186}
187
188void
189free_buf(struct buffer *buf)
190{
191 free(buf->data);
192 CLEAR(*buf);
193}
194
195static void
196free_buf_gc(struct buffer *buf, struct gc_arena *gc)
197{
198 if (gc)
199 {
200 struct gc_entry **e = &gc->list;
201
202 while (*e)
203 {
204 /* check if this object is the one we want to delete */
205 if ((uint8_t *)(*e + 1) == buf->data)
206 {
207 struct gc_entry *to_delete = *e;
208
209 /* remove element from linked list and free it */
210 *e = (*e)->next;
211 free(to_delete);
212
213 break;
214 }
215
216 e = &(*e)->next;
217 }
218 }
219
220 CLEAR(*buf);
221}
222
223/*
224 * Return a buffer for write that is a subset of another buffer
225 */
226struct buffer
227buf_sub(struct buffer *buf, int size, bool prepend)
228{
229 struct buffer ret;
230 uint8_t *data;
231
232 CLEAR(ret);
233 data = prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size);
234 if (data)
235 {
236 ret.capacity = size;
237 ret.data = data;
238 }
239 return ret;
240}
241
242/*
243 * printf append to a buffer with overflow check
244 */
245bool
246buf_printf(struct buffer *buf, const char *format, ...)
247{
248 int ret = false;
249 if (buf_defined(buf))
250 {
252 uint8_t *ptr = BEND(buf);
253 int cap = buf_forward_capacity(buf);
254
255 if (cap > 0)
256 {
257 int stat;
259 stat = vsnprintf((char *)ptr, cap, format, arglist);
261 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
262 buf->len += (int)strlen((char *)ptr);
263 if (stat >= 0 && stat < cap)
264 {
265 ret = true;
266 }
267 }
268 }
269 return ret;
270}
271
272bool
273buf_puts(struct buffer *buf, const char *str)
274{
275 int ret = false;
276 uint8_t *ptr = BEND(buf);
277 int cap = buf_forward_capacity(buf);
278 if (cap > 0)
279 {
280 strncpynt((char *)ptr, str, cap);
281 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
282 buf->len += (int)strlen((char *)ptr);
283 ret = true;
284 }
285 return ret;
286}
287
288/*
289 * write a string to the end of a buffer that was
290 * truncated by buf_printf
291 */
292void
293buf_catrunc(struct buffer *buf, const char *str)
294{
295 if (buf_forward_capacity(buf) <= 1)
296 {
297 size_t len = strlen(str) + 1;
299 {
300 memcpy(buf->data + buf->capacity - len, str, len);
301 }
302 }
303}
304
305bool
306buffer_write_file(const char *filename, const struct buffer *buf)
307{
308 bool ret = false;
309 int fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
310 if (fd == -1)
311 {
312 msg(M_ERRNO, "Cannot open file '%s' for write", filename);
313 return false;
314 }
315
316 const ssize_t size = write(fd, BPTR(buf), (unsigned int)BLEN(buf));
317 if (size != BLEN(buf))
318 {
319 msg(M_ERRNO, "Write error on file '%s'", filename);
320 goto cleanup;
321 }
322
323 ret = true;
324cleanup:
325 if (close(fd) < 0)
326 {
327 msg(M_ERRNO, "Close error on file %s", filename);
328 ret = false;
329 }
330 return ret;
331}
332
333/*
334 * Garbage collection
335 */
336
337void *
338#ifdef DMALLOC
339gc_malloc_debug(size_t size, bool clear, struct gc_arena *a, const char *file, int line)
340#else
341gc_malloc(size_t size, bool clear, struct gc_arena *a)
342#endif
343{
344 void *ret;
345 if (a)
346 {
347 struct gc_entry *e;
348#ifdef DMALLOC
349 e = (struct gc_entry *)openvpn_dmalloc(file, line, size + sizeof(struct gc_entry));
350#else
351 e = (struct gc_entry *)malloc(size + sizeof(struct gc_entry));
352#endif
354 ret = (char *)e + sizeof(struct gc_entry);
355 e->next = a->list;
356 a->list = e;
357 }
358 else
359 {
360#ifdef DMALLOC
361 ret = openvpn_dmalloc(file, line, size);
362#else
363 ret = malloc(size);
364#endif
366 }
367#ifndef ZERO_BUFFER_ON_ALLOC
368 if (clear)
369#endif
370 memset(ret, 0, size);
371 return ret;
372}
373
374void *
375gc_realloc(void *ptr, size_t size, struct gc_arena *a)
376{
377 void *ret = realloc(ptr, size);
379 if (a)
380 {
381 if (ptr && ptr != ret)
382 {
383 /* find the old entry and modify it if realloc changed
384 * the pointer */
385 struct gc_entry_special *e = NULL;
386 for (e = a->list_special; e != NULL; e = e->next)
387 {
388 if (e->addr == ptr)
389 {
390 break;
391 }
392 }
393 ASSERT(e);
394 ASSERT(e->addr == ptr);
395 e->addr = ret;
396 }
397 else if (!ptr)
398 {
399 /* sets e->addr to newptr */
400 gc_addspecial(ret, free, a);
401 }
402 }
403
404 return ret;
405}
406
407void
409{
410 struct gc_entry *e;
411 e = a->list;
412 a->list = NULL;
413
414 while (e != NULL)
415 {
416 struct gc_entry *next = e->next;
417 free(e);
418 e = next;
419 }
420}
421
422/*
423 * Functions to handle special objects in gc_entries
424 */
425
426void
428{
429 struct gc_entry_special *e;
430 e = a->list_special;
431 a->list_special = NULL;
432
433 while (e != NULL)
434 {
435 struct gc_entry_special *next = e->next;
436 e->free_fnc(e->addr);
437 free(e);
438 e = next;
439 }
440}
441
442void
443gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a)
444{
445 ASSERT(a);
446 struct gc_entry_special *e;
447#ifdef DMALLOC
448 e = (struct gc_entry_special *)openvpn_dmalloc(file, line, sizeof(struct gc_entry_special));
449#else
450 e = (struct gc_entry_special *)malloc(sizeof(struct gc_entry_special));
451#endif
453 e->free_fnc = free_function;
454 e->addr = addr;
455
456 e->next = a->list_special;
457 a->list_special = e;
458}
459
460
461/*
462 * Transfer src arena to dest, resetting src to an empty arena.
463 */
464void
465gc_transfer(struct gc_arena *dest, struct gc_arena *src)
466{
467 if (dest && src)
468 {
469 struct gc_entry *e = src->list;
470 if (e)
471 {
472 while (e->next != NULL)
473 {
474 e = e->next;
475 }
476 e->next = dest->list;
477 dest->list = src->list;
478 src->list = NULL;
479 }
480 }
481}
482
483/*
484 * Hex dump -- Output a binary buffer to a hex string and return it.
485 */
486
487char *
488format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags,
489 const char *separator, struct gc_arena *gc)
490{
491 const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
492 const size_t separator_len = separator ? strlen(separator) : 0;
493 const size_t out_len = maxoutput > 0
494 ? maxoutput
495 : ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
496
497 struct buffer out = alloc_buf_gc(out_len, gc);
498 for (size_t i = 0; i < size; ++i)
499 {
500 if (separator && i && !(i % bytes_per_hexblock))
501 {
502 buf_printf(&out, "%s", separator);
503 }
505 {
506 buf_printf(&out, "%02X", data[i]);
507 }
508 else
509 {
510 buf_printf(&out, "%02x", data[i]);
511 }
512 }
513 buf_catrunc(&out, "[more...]");
514 return (char *)out.data;
515}
516
517/*
518 * remove specific trailing character
519 */
520
521void
523{
524 uint8_t *cp = BLAST(buf);
525 if (cp && *cp == remove)
526 {
527 *cp = '\0';
528 --buf->len;
529 }
530}
531
532/*
533 * force a null termination even it requires
534 * truncation of the last char.
535 */
536void
538{
539 char *last = (char *)BLAST(buf);
540 if (last && *last == '\0') /* already terminated? */
541 {
542 return;
543 }
544
545 if (!buf_safe(buf, 1)) /* make space for trailing null */
546 {
547 buf_inc_len(buf, -1);
548 }
549
550 buf_write_u8(buf, 0);
551}
552
553/*
554 * Remove trailing \r and \n chars and ensure
555 * null termination.
556 */
557void
558buf_chomp(struct buffer *buf)
559{
560 while (true)
561 {
562 char *last = (char *)BLAST(buf);
563 if (!last)
564 {
565 break;
566 }
567 if (char_class((unsigned char)*last, CC_CRLF | CC_NULL))
568 {
569 if (!buf_inc_len(buf, -1))
570 {
571 break;
572 }
573 }
574 else
575 {
576 break;
577 }
578 }
580}
581
582const char *
584{
585 while (*str)
586 {
587 const char c = *str;
588 if (!(c == ' ' || c == '\t'))
589 {
590 break;
591 }
592 ++str;
593 }
594 return str;
595}
596
597/*
598 * like buf_null_terminate, but operate on strings
599 */
600void
602{
604 if (len < capacity)
605 {
606 *(str + len) = '\0';
607 }
608 else if (len == capacity)
609 {
610 *(str + len - 1) = '\0';
611 }
612}
613
614/*
615 * Remove trailing \r and \n chars.
616 */
617void
618chomp(char *str)
619{
620 rm_trailing_chars(str, "\r\n");
621}
622
623/*
624 * Remove trailing chars
625 */
626void
628{
629 bool modified;
630 do
631 {
632 const size_t len = strlen(str);
633 modified = false;
634 if (len > 0)
635 {
636 char *cp = str + (len - 1);
637 if (strchr(what_to_delete, *cp) != NULL)
638 {
639 *cp = '\0';
640 modified = true;
641 }
642 }
643 } while (modified);
644}
645
646/*
647 * Allocate a string
648 */
649char *
650#ifdef DMALLOC
651string_alloc_debug(const char *str, struct gc_arena *gc, const char *file, int line)
652#else
653string_alloc(const char *str, struct gc_arena *gc)
654#endif
655{
656 if (str)
657 {
658 const size_t n = strlen(str) + 1;
659 char *ret;
660
661 if (gc)
662 {
663#ifdef DMALLOC
664 ret = (char *)gc_malloc_debug(n, false, gc, file, line);
665#else
666 ret = (char *)gc_malloc(n, false, gc);
667#endif
668 }
669 else
670 {
671 /* If there are no garbage collector available, it's expected
672 * that the caller cleans up afterwards. This is coherent with the
673 * earlier behaviour when gc_malloc() would be called with gc == NULL
674 */
675#ifdef DMALLOC
676 ret = openvpn_dmalloc(file, line, n);
677#else
678 ret = calloc(1, n);
679#endif
681 }
682 memcpy(ret, str, n);
683 return ret;
684 }
685 else
686 {
687 return NULL;
688 }
689}
690
691/*
692 * Erase all characters in a string
693 */
694void
696{
697 if (str)
698 {
700 }
701}
702
703/*
704 * Return the length of a string array
705 */
706int
707string_array_len(const char **array)
708{
709 int i = 0;
710 if (array)
711 {
712 while (array[i])
713 {
714 ++i;
715 }
716 }
717 return i;
718}
719
720char *
721print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
722{
723 struct buffer out = alloc_buf_gc(256, gc);
724 int i = 0;
725 for (;;)
726 {
727 const char *cp = *p++;
728 if (!cp)
729 {
730 break;
731 }
732 if (i)
733 {
734 buf_printf(&out, " ");
735 }
736 if (flags & PA_BRACKET)
737 {
738 buf_printf(&out, "[%s]", cp);
739 }
740 else
741 {
742 buf_printf(&out, "%s", cp);
743 }
744 ++i;
745 }
746 return BSTR(&out);
747}
748
749/*
750 * Allocate a string inside a buffer
751 */
752struct buffer
754string_alloc_buf_debug(const char *str, struct gc_arena *gc, const char *file, int line)
755#else
756string_alloc_buf(const char *str, struct gc_arena *gc)
757#endif
758{
759 struct buffer buf;
760
761 ASSERT(str);
762
763#ifdef DMALLOC
764 buf_set_read(&buf, (uint8_t *)string_alloc_debug(str, gc, file, line), strlen(str) + 1);
765#else
766 buf_set_read(&buf, (uint8_t *)string_alloc(str, gc), strlen(str) + 1);
767#endif
768
769 if (buf.len > 0) /* Don't count trailing '\0' as part of length */
770 {
771 --buf.len;
772 }
773
774 return buf;
775}
776
777/*
778 * String comparison
779 */
780
781bool
782buf_string_match_head_str(const struct buffer *src, const char *match)
783{
784 const size_t size = strlen(match);
785 if (!buf_size_valid(size) || (int)size > src->len)
786 {
787 return false;
788 }
789 return memcmp(BPTR(src), match, size) == 0;
790}
791
792bool
794{
796 {
798 return true;
799 }
800 else
801 {
802 return false;
803 }
804}
805
806int
807buf_substring_len(const struct buffer *buf, int delim)
808{
809 int i = 0;
810 struct buffer tmp = *buf;
811 int c;
812
813 while ((c = buf_read_u8(&tmp)) >= 0)
814 {
815 ++i;
816 if (c == delim)
817 {
818 return i;
819 }
820 }
821 return -1;
822}
823
824/*
825 * String parsing
826 */
827
828bool
829buf_parse(struct buffer *buf, const int delim, char *line, const int size)
830{
831 bool eol = false;
832 int n = 0;
833 int c;
834
835 ASSERT(size > 0);
836
837 do
838 {
839 c = buf_peek_u8(buf);
840 if (c < 0)
841 {
842 eol = true;
843 line[n] = 0;
844 break;
845 }
846 if (c == delim)
847 {
848 buf_advance(buf, 1);
849 line[n] = 0;
850 break;
851 }
852 if (n >= (size - 1))
853 {
854 break;
855 }
856 buf_advance(buf, 1);
857 line[n++] = (char)c;
858 } while (c);
859
860 line[size - 1] = '\0';
861 return !(eol && !strlen(line));
862}
863
864/*
865 * Print a string which might be NULL
866 */
867const char *
868np(const char *str)
869{
870 if (str)
871 {
872 return str;
873 }
874 else
875 {
876 return "[NULL]";
877 }
878}
879
880/*
881 * Classify and mutate strings based on character types.
882 */
883
884/* Note 1: This functions depends on getting an unsigned
885 char. Both the is*() functions and our own checks expect it
886 this way.
887 Note 2: For CC_PRINT we just accept everything >= 32, so
888 if we ingest non-ASCII UTF-8 we will classify it as
889 printable since it will be >= 128. Other encodings are
890 not officially supported.
891*/
892bool
893char_class(const unsigned char c, const unsigned int flags)
894{
895 if (!flags)
896 {
897 return false;
898 }
899 if (flags & CC_ANY)
900 {
901 return true;
902 }
903
904 if ((flags & CC_NULL) && c == '\0')
905 {
906 return true;
907 }
908
909 if ((flags & CC_ALNUM) && isalnum(c))
910 {
911 return true;
912 }
913 if ((flags & CC_ALPHA) && isalpha(c))
914 {
915 return true;
916 }
917 if ((flags & CC_ASCII) && isascii(c))
918 {
919 return true;
920 }
921 if ((flags & CC_CNTRL) && iscntrl(c))
922 {
923 return true;
924 }
925 if ((flags & CC_DIGIT) && isdigit(c))
926 {
927 return true;
928 }
929 /* allow ascii non-control and UTF-8, consider DEL to be a control */
930 if ((flags & CC_PRINT) && (c >= 32 && c != 127))
931 {
932 return true;
933 }
934 if ((flags & CC_PUNCT) && ispunct(c))
935 {
936 return true;
937 }
938 if ((flags & CC_SPACE) && isspace(c))
939 {
940 return true;
941 }
942 if ((flags & CC_XDIGIT) && isxdigit(c))
943 {
944 return true;
945 }
946
947 if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
948 {
949 return true;
950 }
951 if ((flags & CC_NEWLINE) && c == '\n')
952 {
953 return true;
954 }
955 if ((flags & CC_CR) && c == '\r')
956 {
957 return true;
958 }
959
960 if ((flags & CC_BACKSLASH) && c == '\\')
961 {
962 return true;
963 }
964 if ((flags & CC_UNDERBAR) && c == '_')
965 {
966 return true;
967 }
968 if ((flags & CC_DASH) && c == '-')
969 {
970 return true;
971 }
972 if ((flags & CC_DOT) && c == '.')
973 {
974 return true;
975 }
976 if ((flags & CC_COMMA) && c == ',')
977 {
978 return true;
979 }
980 if ((flags & CC_COLON) && c == ':')
981 {
982 return true;
983 }
984 if ((flags & CC_SLASH) && c == '/')
985 {
986 return true;
987 }
988 if ((flags & CC_SINGLE_QUOTE) && c == '\'')
989 {
990 return true;
991 }
992 if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
993 {
994 return true;
995 }
996 if ((flags & CC_REVERSE_QUOTE) && c == '`')
997 {
998 return true;
999 }
1000 if ((flags & CC_AT) && c == '@')
1001 {
1002 return true;
1003 }
1004 if ((flags & CC_EQUAL) && c == '=')
1005 {
1006 return true;
1007 }
1008 if ((flags & CC_LESS_THAN) && c == '<')
1009 {
1010 return true;
1011 }
1012 if ((flags & CC_GREATER_THAN) && c == '>')
1013 {
1014 return true;
1015 }
1016 if ((flags & CC_PIPE) && c == '|')
1017 {
1018 return true;
1019 }
1020 if ((flags & CC_QUESTION_MARK) && c == '?')
1021 {
1022 return true;
1023 }
1024 if ((flags & CC_ASTERISK) && c == '*')
1025 {
1026 return true;
1027 }
1028
1029 return false;
1030}
1031
1032static inline bool
1033char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
1034{
1035 return char_class((unsigned char)c, inclusive)
1036 && !char_class((unsigned char)c, exclusive);
1037}
1038
1039bool
1040string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
1041{
1042 char c;
1043 ASSERT(str);
1044 while ((c = *str++))
1045 {
1047 {
1048 return false;
1049 }
1050 }
1051 return true;
1052}
1053
1054/*
1055 * Modify string in place.
1056 * Guaranteed to not increase string length.
1057 */
1058bool
1059string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive,
1060 const char replace)
1061{
1062 const char *in = str;
1063 bool ret = true;
1064
1065 ASSERT(str);
1066
1067 while (true)
1068 {
1069 char c = *in++;
1070 if (c)
1071 {
1073 {
1074 c = replace;
1075 ret = false;
1076 }
1077 if (c)
1078 {
1079 *str++ = c;
1080 }
1081 }
1082 else
1083 {
1084 *str = '\0';
1085 break;
1086 }
1087 }
1088 return ret;
1089}
1090
1091bool
1092string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
1093{
1094 ASSERT(buf);
1095
1096 for (int i = 0; i < BLEN(buf); i++)
1097 {
1098 char c = BSTR(buf)[i];
1099
1101 {
1102 return false;
1103 }
1104 }
1105 return true;
1106}
1107
1108const char *
1109string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive,
1110 const char replace, struct gc_arena *gc)
1111{
1112 if (str)
1113 {
1114 char *buf = string_alloc(str, gc);
1116 return buf;
1117 }
1118 else
1119 {
1120 return NULL;
1121 }
1122}
1123
1124void
1125string_replace_leading(char *str, const char match, const char replace)
1126{
1127 ASSERT(match != '\0');
1128 while (*str)
1129 {
1130 if (*str == match)
1131 {
1132 *str = replace;
1133 }
1134 else
1135 {
1136 break;
1137 }
1138 ++str;
1139 }
1140}
1141
1142bool
1143checked_snprintf(char *str, size_t size, const char *format, ...)
1144{
1147 ASSERT(size < INT_MAX);
1148 int len = vsnprintf(str, size, format, arglist);
1149 va_end(arglist);
1150 return (len >= 0 && len < (ssize_t)size);
1151}
1152
1153#ifdef VERIFY_ALIGNMENT
1154void
1155valign4(const struct buffer *buf, const char *file, const int line)
1156{
1157 if (buf && buf->len)
1158 {
1159 msglvl_t msglevel = D_ALIGN_DEBUG;
1160 const uintptr_t u = (uintptr_t)BPTR(buf);
1161
1162 if (u & (PAYLOAD_ALIGN - 1))
1163 {
1164 msglevel = D_ALIGN_ERRORS;
1165 }
1166
1167 msg(msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
1168 (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "", file, line, (ptr_type)buf->data,
1169 buf->offset, buf->len, buf->capacity, buf_debug_file(buf), buf_debug_line(buf));
1170 }
1171}
1172#endif /* ifdef VERIFY_ALIGNMENT */
1173
1174/*
1175 * struct buffer_list
1176 */
1177struct buffer_list *
1179{
1180 struct buffer_list *ret;
1181 ALLOC_OBJ_CLEAR(ret, struct buffer_list);
1182 ret->size = 0;
1183 return ret;
1184}
1185
1186void
1188{
1189 if (ol)
1190 {
1192 free(ol);
1193 }
1194}
1195
1196bool
1198{
1199 return ol && ol->head != NULL && ol->size > 0;
1200}
1201
1202void
1204{
1205 struct buffer_entry *e = ol->head;
1206 while (e)
1207 {
1208 struct buffer_entry *next = e->next;
1209 free_buf(&e->buf);
1210 free(e);
1211 e = next;
1212 }
1213 ol->head = ol->tail = NULL;
1214 ol->size = 0;
1215}
1216
1217void
1218buffer_list_push(struct buffer_list *ol, const char *str)
1219{
1220 if (str)
1221 {
1222 const size_t len = strlen((const char *)str);
1223 struct buffer_entry *e = buffer_list_push_data(ol, str, len + 1);
1224 if (e)
1225 {
1226 e->buf.len--; /* Don't count trailing '\0' as part of length */
1227 }
1228 }
1229}
1230
1231struct buffer_entry *
1232buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
1233{
1234 struct buffer_entry *e = NULL;
1235 if (data)
1236 {
1237 ALLOC_OBJ_CLEAR(e, struct buffer_entry);
1238
1239 ++ol->size;
1240 if (ol->tail)
1241 {
1242 ASSERT(ol->head);
1243 ol->tail->next = e;
1244 }
1245 else
1246 {
1247 ASSERT(!ol->head);
1248 ol->head = e;
1249 }
1250 e->buf = alloc_buf(size);
1251 memcpy(e->buf.data, data, size);
1252 /* Note: size implicitly checked by alloc_buf */
1253 e->buf.len = (int)size;
1254 ol->tail = e;
1255 }
1256 return e;
1257}
1258
1259struct buffer *
1261{
1262 if (ol && ol->head)
1263 {
1264 return &ol->head->buf;
1265 }
1266 else
1267 {
1268 return NULL;
1269 }
1270}
1271
1272void
1273buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
1274{
1275 const size_t sep_len = strlen(sep);
1276 struct buffer_entry *more = bl->head;
1277 size_t size = 0;
1278 size_t count = 0;
1279 for (; more; ++count)
1280 {
1281 size_t extra_len = BLENZ(&more->buf) + sep_len;
1282 if (size + extra_len > max_len)
1283 {
1284 break;
1285 }
1286
1287 size += extra_len;
1288 more = more->next;
1289 }
1290
1291 if (count >= 2)
1292 {
1293 struct buffer_entry *f;
1294 ALLOC_OBJ_CLEAR(f, struct buffer_entry);
1295 f->buf = alloc_buf(size + 1); /* prevent 0-byte malloc */
1296
1297 struct buffer_entry *e = bl->head;
1298 for (size_t i = 0; e && i < count; ++i)
1299 {
1300 struct buffer_entry *next = e->next;
1301 buf_copy(&f->buf, &e->buf);
1302 buf_write(&f->buf, sep, sep_len);
1303 free_buf(&e->buf);
1304 free(e);
1305 e = next;
1306 }
1307 bl->head = f;
1308 bl->size -= count - 1;
1309 f->next = more;
1310 if (!more)
1311 {
1312 bl->tail = f;
1313 }
1314 }
1315}
1316
1317void
1318buffer_list_aggregate(struct buffer_list *bl, const size_t max)
1319{
1321}
1322
1323void
1325{
1326 if (buffer_list_defined(ol))
1327 {
1328 struct buffer_entry *e = ol->head->next;
1329 free_buf(&ol->head->buf);
1330 free(ol->head);
1331 ol->head = e;
1332 --ol->size;
1333 if (!e)
1334 {
1335 ol->tail = NULL;
1336 }
1337 }
1338}
1339
1340void
1341buffer_list_advance(struct buffer_list *ol, ssize_t n)
1342{
1343 if (ol->head)
1344 {
1345 struct buffer *buf = &ol->head->buf;
1346 ASSERT(buf_advance(buf, n));
1347 if (!BLEN(buf))
1348 {
1350 }
1351 }
1352}
1353
1354struct buffer_list *
1355buffer_list_file(const char *fn, int max_line_len)
1356{
1357 FILE *fp = platform_fopen(fn, "r");
1358 struct buffer_list *bl = NULL;
1359
1360 if (fp)
1361 {
1362 char *line = (char *)malloc(max_line_len);
1363 if (line)
1364 {
1365 bl = buffer_list_new();
1366 while (fgets(line, max_line_len, fp) != NULL)
1367 {
1368 buffer_list_push(bl, line);
1369 }
1370 free(line);
1371 }
1372 fclose(fp);
1373 }
1374 return bl;
1375}
1376
1377struct buffer
1379{
1380 struct buffer ret = { 0 };
1381
1382 platform_stat_t file_stat = { 0 };
1383 if (platform_stat(filename, &file_stat) < 0)
1384 {
1385 return ret;
1386 }
1387
1388 FILE *fp = platform_fopen(filename, "r");
1389 if (!fp)
1390 {
1391 return ret;
1392 }
1393
1394 const size_t size = file_stat.st_size;
1395 ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */
1396 size_t read_size = fread(BPTR(&ret), 1, size, fp);
1397 if (read_size == 0)
1398 {
1399 free_buf_gc(&ret, gc);
1400 goto cleanup;
1401 }
1404
1405cleanup:
1406 fclose(fp);
1407 return ret;
1408}
bool buffer_list_defined(const struct buffer_list *ol)
Checks if the list is valid and non-empty.
Definition buffer.c:1197
bool buf_string_compare_advance(struct buffer *src, const char *match)
Definition buffer.c:793
struct buffer_entry * buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
Allocates and appends a new buffer containing data of length size.
Definition buffer.c:1232
void rm_trailing_chars(char *str, const char *what_to_delete)
Definition buffer.c:627
void string_null_terminate(char *str, int len, int capacity)
Definition buffer.c:601
void free_buf(struct buffer *buf)
Definition buffer.c:189
void buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
Aggregates as many buffers as possible from bl in a new buffer of maximum length max_len .
Definition buffer.c:1273
static bool char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
Definition buffer.c:1033
void buf_clear(struct buffer *buf)
Definition buffer.c:168
void buffer_list_reset(struct buffer_list *ol)
Empty the list ol and frees all the contained buffers.
Definition buffer.c:1203
static void free_buf_gc(struct buffer *buf, struct gc_arena *gc)
Definition buffer.c:196
const char * skip_leading_whitespace(const char *str)
Definition buffer.c:583
void buffer_list_aggregate(struct buffer_list *bl, const size_t max)
Aggregates as many buffers as possible from bl in a new buffer of maximum length max_len .
Definition buffer.c:1318
struct buffer clone_buf(const struct buffer *buf)
Definition buffer.c:115
void x_gc_freespecial(struct gc_arena *a)
Definition buffer.c:427
bool buffer_write_file(const char *filename, const struct buffer *buf)
Write buffer contents to file.
Definition buffer.c:306
void buf_catrunc(struct buffer *buf, const char *str)
Definition buffer.c:293
#define buf_debug_file(buf)
Definition buffer.c:163
void buffer_list_pop(struct buffer_list *ol)
Definition buffer.c:1324
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:246
void string_replace_leading(char *str, const char match, const char replace)
Definition buffer.c:1125
bool buf_puts(struct buffer *buf, const char *str)
Definition buffer.c:273
struct buffer_list * buffer_list_file(const char *fn, int max_line_len)
Definition buffer.c:1355
void string_clear(char *str)
Definition buffer.c:695
#define buf_debug_line(buf)
Definition buffer.c:162
void gc_transfer(struct gc_arena *dest, struct gc_arena *src)
Definition buffer.c:465
bool string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
Definition buffer.c:1040
char * print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
Definition buffer.c:721
void buf_null_terminate(struct buffer *buf)
Definition buffer.c:537
struct buffer buf_sub(struct buffer *buf, int size, bool prepend)
Definition buffer.c:227
void * gc_realloc(void *ptr, size_t size, struct gc_arena *a)
allows to realloc a pointer previously allocated by gc_malloc or gc_realloc
Definition buffer.c:375
struct buffer_list * buffer_list_new(void)
Allocate an empty buffer list of capacity max_size.
Definition buffer.c:1178
void chomp(char *str)
Definition buffer.c:618
struct buffer * buffer_list_peek(struct buffer_list *ol)
Retrieve the head buffer.
Definition buffer.c:1260
const char * np(const char *str)
Definition buffer.c:868
bool string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
Check a buffer if it only consists of allowed characters.
Definition buffer.c:1092
size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra)
Definition buffer.c:40
void buffer_list_free(struct buffer_list *ol)
Frees a buffer list and all the buffers in it.
Definition buffer.c:1187
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition buffer.c:341
bool buf_assign(struct buffer *dest, const struct buffer *src)
Definition buffer.c:179
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:88
char * format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags, const char *separator, struct gc_arena *gc)
Definition buffer.c:488
bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
Modifies a string in place by replacing certain classes of characters of it with a specified characte...
Definition buffer.c:1059
struct buffer alloc_buf(size_t size)
Definition buffer.c:63
const char * string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace, struct gc_arena *gc)
Returns a copy of a string with certain classes of characters of it replaced with a specified charact...
Definition buffer.c:1109
void gc_addspecial(void *addr, void(*free_function)(void *), struct gc_arena *a)
Definition buffer.c:443
void buffer_list_advance(struct buffer_list *ol, ssize_t n)
Definition buffer.c:1341
int string_array_len(const char **array)
Definition buffer.c:707
struct buffer buffer_read_from_file(const char *filename, struct gc_arena *gc)
buffer_read_from_file - copy the content of a file into a buffer
Definition buffer.c:1378
void buf_rmtail(struct buffer *buf, uint8_t remove)
Definition buffer.c:522
bool buf_parse(struct buffer *buf, const int delim, char *line, const int size)
Definition buffer.c:829
bool checked_snprintf(char *str, size_t size, const char *format,...)
Like snprintf() but returns an boolean.
Definition buffer.c:1143
void buf_size_error(const size_t size)
Definition buffer.c:54
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:653
struct buffer string_alloc_buf(const char *str, struct gc_arena *gc)
Definition buffer.c:756
void buffer_list_push(struct buffer_list *ol, const char *str)
Allocates and appends a new buffer containing str as data to ol.
Definition buffer.c:1218
void x_gc_free(struct gc_arena *a)
Definition buffer.c:408
void buf_chomp(struct buffer *buf)
Definition buffer.c:558
bool char_class(const unsigned char c, const unsigned int flags)
Definition buffer.c:893
bool buf_string_match_head_str(const struct buffer *src, const char *match)
Definition buffer.c:782
int buf_substring_len(const struct buffer *buf, int delim)
Definition buffer.c:807
#define CC_COMMA
comma
Definition buffer.h:899
#define BEND(buf)
Definition buffer.h:124
#define CC_DASH
dash
Definition buffer.h:897
static bool buf_size_valid(const size_t size)
Definition buffer.h:286
#define CC_DOUBLE_QUOTE
double quote
Definition buffer.h:903
#define BLAST(buf)
Definition buffer.h:125
#define CC_BLANK
space or tab
Definition buffer.h:891
static bool buf_init_dowork(struct buffer *buf, int offset)
Definition buffer.h:320
#define CC_PIPE
pipe
Definition buffer.h:909
#define BSTR(buf)
Definition buffer.h:129
static bool buf_copy(struct buffer *dest, const struct buffer *src)
Definition buffer.h:705
#define CC_ANY
any character
Definition buffer.h:878
#define BPTR(buf)
Definition buffer.h:123
#define CC_AT
at sign
Definition buffer.h:905
static int buf_peek_u8(struct buffer *buf)
Definition buffer.h:775
#define CC_XDIGIT
hex digit isxdigit()
Definition buffer.h:889
#define CC_COLON
colon
Definition buffer.h:900
#define CC_SINGLE_QUOTE
single quote
Definition buffer.h:902
static bool buf_inc_len(struct buffer *buf, int inc)
Definition buffer.h:589
#define CC_DIGIT
digit isdigit()
Definition buffer.h:885
#define PA_BRACKET
Definition buffer.h:144
#define CC_DOT
dot
Definition buffer.h:898
#define CC_CRLF
carriage return or newline
Definition buffer.h:915
static bool buf_safe(const struct buffer *buf, size_t len)
Definition buffer.h:519
#define ALLOC_SIZE_MAX
Definition buffer.h:1078
#define CC_ASTERISK
asterisk
Definition buffer.h:911
#define CC_ALPHA
alphabetic isalpha()
Definition buffer.h:882
#define CC_NEWLINE
newline
Definition buffer.h:892
static uint8_t * buf_prepend(struct buffer *buf, ssize_t size)
Definition buffer.h:605
static void buf_set_read(struct buffer *buf, const uint8_t *data, size_t size)
Definition buffer.h:349
static int buf_forward_capacity(const struct buffer *buf)
Definition buffer.h:540
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:415
static uint8_t * buf_write_alloc(struct buffer *buf, size_t size)
Definition buffer.h:634
static bool buf_advance(struct buffer *buf, ssize_t size)
Definition buffer.h:617
#define CC_REVERSE_QUOTE
reverse quote
Definition buffer.h:904
#define CC_SPACE
whitespace isspace()
Definition buffer.h:888
#define CC_ASCII
ASCII character.
Definition buffer.h:883
static bool buf_write(struct buffer *dest, const void *src, size_t size)
Definition buffer.h:661
#define CC_BACKSLASH
backslash
Definition buffer.h:895
#define CC_CNTRL
control character iscntrl()
Definition buffer.h:884
#define CC_LESS_THAN
less than sign
Definition buffer.h:907
static bool buf_write_u8(struct buffer *dest, uint8_t data)
Definition buffer.h:685
static int buf_read_u8(struct buffer *buf)
Definition buffer.h:787
#define BLEN(buf)
Definition buffer.h:126
#define CC_CR
carriage return
Definition buffer.h:893
#define CC_SLASH
slash
Definition buffer.h:901
#define CC_UNDERBAR
underscore
Definition buffer.h:896
#define CC_GREATER_THAN
greater than sign
Definition buffer.h:908
#define BLENZ(buf)
Definition buffer.h:127
static void strncpynt(char *dest, const char *src, size_t maxlen)
Definition buffer.h:362
static void check_malloc_return(void *p)
Definition buffer.h:1131
#define CC_NULL
null character \0
Definition buffer.h:879
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:886
#define CC_QUESTION_MARK
question mark
Definition buffer.h:910
#define ALLOC_OBJ_CLEAR(dptr, type)
Definition buffer.h:1088
static bool buf_defined(const struct buffer *buf)
Definition buffer.h:229
#define CC_ALNUM
alphanumeric isalnum()
Definition buffer.h:881
#define buf_init(buf, offset)
Definition buffer.h:210
#define CC_EQUAL
equal sign
Definition buffer.h:906
#define FHE_SPACE_BREAK_MASK
Definition buffer.h:498
#define CC_PUNCT
punctuation ispunct()
Definition buffer.h:887
#define FHE_CAPS
Definition buffer.h:499
static int buf_forward_capacity_total(const struct buffer *buf)
Definition buffer.h:558
unsigned long ptr_type
Definition common.h:59
#define ptr_format
Definition common.h:50
#define D_ALIGN_ERRORS
Definition errlevel.h:69
#define D_ALIGN_DEBUG
Definition errlevel.h:141
@ write
#define PAYLOAD_ALIGN
Definition mtu.h:100
#define CLEAR(x)
Definition basic.h:32
#define M_FATAL
Definition error.h:90
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:219
#define M_ERRNO
Definition error.h:95
FILE * platform_fopen(const char *path, const char *mode)
Definition platform.c:500
int platform_open(const char *path, int flags, int mode)
Definition platform.c:513
int platform_stat(const char *path, platform_stat_t *buf)
Definition platform.c:526
struct _stat platform_stat_t
Definition platform.h:118
Definition buffer.h:1143
struct buffer_entry * next
Definition buffer.h:1145
struct buffer buf
Definition buffer.h:1144
size_t size
Definition buffer.h:1152
struct buffer_entry * tail
Definition buffer.h:1151
struct buffer_entry * head
Definition buffer.h:1150
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
int capacity
Size in bytes of memory allocated by malloc().
Definition buffer.h:61
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:67
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
int offset
Offset in bytes of the actual content within the allocated memory.
Definition buffer.h:63
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
struct gc_entry_special * list_special
Definition buffer.h:119
struct gc_entry * list
First element of the linked list of gc_entry structures.
Definition buffer.h:117
Garbage collection entry for a specially allocated structure that needs a custom free function to be ...
Definition buffer.h:98
void(* free_fnc)(void *)
Definition buffer.h:100
void * addr
Definition buffer.h:101
struct gc_entry_special * next
Definition buffer.h:99
Garbage collection entry for one dynamically allocated block of memory.
Definition buffer.h:87
struct gc_entry * next
Pointer to the next item in the linked list.
Definition buffer.h:88
#define unlikely(x)
Definition syshead.h:35
static int cleanup(void **state)
struct gc_arena gc
Definition test_ssl.c:133