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 > (unsigned long long)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=%lu", (unsigned long)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
68 if (!buf_size_valid(size))
69 {
70 buf_size_error(size);
71 }
72 buf.capacity = (int)size;
73 buf.offset = 0;
74 buf.len = 0;
75#ifdef DMALLOC
76 buf.data = openvpn_dmalloc(file, line, size);
77#else
78 buf.data = calloc(1, size);
79#endif
81
82 return buf;
83}
84
85struct buffer
87alloc_buf_gc_debug(size_t size, struct gc_arena *gc, const char *file, int line)
88#else
89alloc_buf_gc(size_t size, struct gc_arena *gc)
90#endif
91{
92 struct buffer buf;
93 if (!buf_size_valid(size))
94 {
95 buf_size_error(size);
96 }
97 buf.capacity = (int)size;
98 buf.offset = 0;
99 buf.len = 0;
100#ifdef DMALLOC
101 buf.data = (uint8_t *)gc_malloc_debug(size, false, gc, file, line);
102#else
103 buf.data = (uint8_t *)gc_malloc(size, false, gc);
104#endif
105 if (size)
106 {
107 *buf.data = 0;
108 }
109 return buf;
110}
111
112struct buffer
114clone_buf_debug(const struct buffer *buf, const char *file, int line)
115#else
116clone_buf(const struct buffer *buf)
117#endif
118{
119 struct buffer ret;
120 ret.capacity = buf->capacity;
121 ret.offset = buf->offset;
122 ret.len = buf->len;
123#ifdef DMALLOC
124 ret.data = (uint8_t *)openvpn_dmalloc(file, line, buf->capacity);
125#else
126 ret.data = (uint8_t *)malloc(buf->capacity);
127#endif
129 memcpy(BPTR(&ret), BPTR(buf), BLENZ(buf));
130 return ret;
131}
132
133#ifdef BUF_INIT_TRACKING
134
135bool
136buf_init_debug(struct buffer *buf, int offset, const char *file, int line)
137{
138 buf->debug_file = file;
139 buf->debug_line = line;
140 return buf_init_dowork(buf, offset);
141}
142
143static inline int
144buf_debug_line(const struct buffer *buf)
145{
146 return buf->debug_line;
147}
148
149static const char *
150buf_debug_file(const struct buffer *buf)
151{
152 return buf->debug_file;
153}
154
155#else /* ifdef BUF_INIT_TRACKING */
156
157#define buf_debug_line(buf) 0
158#define buf_debug_file(buf) "[UNDEF]"
159
160#endif /* ifdef BUF_INIT_TRACKING */
161
162void
163buf_clear(struct buffer *buf)
164{
165 if (buf->capacity > 0)
166 {
167 secure_memzero(buf->data, buf->capacity);
168 }
169 buf->len = 0;
170 buf->offset = 0;
171}
172
173bool
174buf_assign(struct buffer *dest, const struct buffer *src)
175{
176 if (!buf_init(dest, src->offset))
177 {
178 return false;
179 }
180 return buf_write(dest, BPTR(src), BLENZ(src));
181}
182
183void
184free_buf(struct buffer *buf)
185{
186 free(buf->data);
187 CLEAR(*buf);
188}
189
190static void
191free_buf_gc(struct buffer *buf, struct gc_arena *gc)
192{
193 if (gc)
194 {
195 struct gc_entry **e = &gc->list;
196
197 while (*e)
198 {
199 /* check if this object is the one we want to delete */
200 if ((uint8_t *)(*e + 1) == buf->data)
201 {
202 struct gc_entry *to_delete = *e;
203
204 /* remove element from linked list and free it */
205 *e = (*e)->next;
206 free(to_delete);
207
208 break;
209 }
210
211 e = &(*e)->next;
212 }
213 }
214
215 CLEAR(*buf);
216}
217
218/*
219 * Return a buffer for write that is a subset of another buffer
220 */
221struct buffer
222buf_sub(struct buffer *buf, int size, bool prepend)
223{
224 struct buffer ret;
225 uint8_t *data;
226
227 CLEAR(ret);
228 data = prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size);
229 if (data)
230 {
231 ret.capacity = size;
232 ret.data = data;
233 }
234 return ret;
235}
236
237/*
238 * printf append to a buffer with overflow check
239 */
240bool
241buf_printf(struct buffer *buf, const char *format, ...)
242{
243 int ret = false;
244 if (buf_defined(buf))
245 {
247 uint8_t *ptr = BEND(buf);
248 int cap = buf_forward_capacity(buf);
249
250 if (cap > 0)
251 {
252 int stat;
254 stat = vsnprintf((char *)ptr, cap, format, arglist);
256 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
257 buf->len += (int)strlen((char *)ptr);
258 if (stat >= 0 && stat < cap)
259 {
260 ret = true;
261 }
262 }
263 }
264 return ret;
265}
266
267bool
268buf_puts(struct buffer *buf, const char *str)
269{
270 int ret = false;
271 uint8_t *ptr = BEND(buf);
272 int cap = buf_forward_capacity(buf);
273 if (cap > 0)
274 {
275 strncpynt((char *)ptr, str, cap);
276 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
277 buf->len += (int)strlen((char *)ptr);
278 ret = true;
279 }
280 return ret;
281}
282
283#if defined(__GNUC__) || defined(__clang__)
284#pragma GCC diagnostic push
285#pragma GCC diagnostic ignored "-Wsign-compare"
286#endif
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 (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 unsigned int u = (unsigned int)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;
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 = (int)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 e->buf.len = (int)size;
1253 ol->tail = e;
1254 }
1255 return e;
1256}
1257
1258struct buffer *
1260{
1261 if (ol && ol->head)
1262 {
1263 return &ol->head->buf;
1264 }
1265 else
1266 {
1267 return NULL;
1268 }
1269}
1270
1271void
1272buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
1273{
1274 const size_t sep_len = strlen(sep);
1275 struct buffer_entry *more = bl->head;
1276 size_t size = 0;
1277 int count = 0;
1278 for (; more; ++count)
1279 {
1280 size_t extra_len = BLENZ(&more->buf) + sep_len;
1281 if (size + extra_len > max_len)
1282 {
1283 break;
1284 }
1285
1286 size += extra_len;
1287 more = more->next;
1288 }
1289
1290 if (count >= 2)
1291 {
1292 struct buffer_entry *f;
1293 ALLOC_OBJ_CLEAR(f, struct buffer_entry);
1294 f->buf = alloc_buf(size + 1); /* prevent 0-byte malloc */
1295
1296 struct buffer_entry *e = bl->head;
1297 for (size_t i = 0; e && i < count; ++i)
1298 {
1299 struct buffer_entry *next = e->next;
1300 buf_copy(&f->buf, &e->buf);
1301 buf_write(&f->buf, sep, sep_len);
1302 free_buf(&e->buf);
1303 free(e);
1304 e = next;
1305 }
1306 bl->head = f;
1307 bl->size -= count - 1;
1308 f->next = more;
1309 if (!more)
1310 {
1311 bl->tail = f;
1312 }
1313 }
1314}
1315
1316#if defined(__GNUC__) || defined(__clang__)
1317#pragma GCC diagnostic pop
1318#endif
1319
1320void
1321buffer_list_aggregate(struct buffer_list *bl, const size_t max)
1322{
1324}
1325
1326void
1328{
1329 if (ol && ol->head)
1330 {
1331 struct buffer_entry *e = ol->head->next;
1332 free_buf(&ol->head->buf);
1333 free(ol->head);
1334 ol->head = e;
1335 --ol->size;
1336 if (!e)
1337 {
1338 ol->tail = NULL;
1339 }
1340 }
1341}
1342
1343void
1344buffer_list_advance(struct buffer_list *ol, ssize_t n)
1345{
1346 if (ol->head)
1347 {
1348 struct buffer *buf = &ol->head->buf;
1349 ASSERT(buf_advance(buf, n));
1350 if (!BLEN(buf))
1351 {
1353 }
1354 }
1355}
1356
1357struct buffer_list *
1358buffer_list_file(const char *fn, int max_line_len)
1359{
1360 FILE *fp = platform_fopen(fn, "r");
1361 struct buffer_list *bl = NULL;
1362
1363 if (fp)
1364 {
1365 char *line = (char *)malloc(max_line_len);
1366 if (line)
1367 {
1368 bl = buffer_list_new();
1369 while (fgets(line, max_line_len, fp) != NULL)
1370 {
1371 buffer_list_push(bl, line);
1372 }
1373 free(line);
1374 }
1375 fclose(fp);
1376 }
1377 return bl;
1378}
1379
1380struct buffer
1382{
1383 struct buffer ret = { 0 };
1384
1385 platform_stat_t file_stat = { 0 };
1386 if (platform_stat(filename, &file_stat) < 0)
1387 {
1388 return ret;
1389 }
1390
1391 FILE *fp = platform_fopen(filename, "r");
1392 if (!fp)
1393 {
1394 return ret;
1395 }
1396
1397 const size_t size = file_stat.st_size;
1398 ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */
1399 size_t read_size = fread(BPTR(&ret), 1, size, fp);
1400 if (read_size == 0)
1401 {
1402 free_buf_gc(&ret, gc);
1403 goto cleanup;
1404 }
1405 ASSERT(buf_inc_len(&ret, (int)read_size));
1406 buf_null_terminate(&ret);
1407
1408cleanup:
1409 fclose(fp);
1410 return ret;
1411}
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:184
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:1272
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:163
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:191
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:1321
struct buffer clone_buf(const struct buffer *buf)
Definition buffer.c:116
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:158
void buffer_list_pop(struct buffer_list *ol)
Definition buffer.c:1327
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:241
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:268
struct buffer_list * buffer_list_file(const char *fn, int max_line_len)
Definition buffer.c:1358
void string_clear(char *str)
Definition buffer.c:695
#define buf_debug_line(buf)
Definition buffer.c:157
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:222
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:1259
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:174
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:89
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:1344
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:1381
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:95
#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
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:131