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-2025 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 = 0xFFFFFFFF;
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), BLEN(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), BLEN(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/*
284 * write a string to the end of a buffer that was
285 * truncated by buf_printf
286 */
287void
288buf_catrunc(struct buffer *buf, const char *str)
289{
290 if (buf_forward_capacity(buf) <= 1)
291 {
292 size_t len = strlen(str) + 1;
294 {
295 memcpy(buf->data + buf->capacity - len, str, len);
296 }
297 }
298}
299
300bool
301buffer_write_file(const char *filename, const struct buffer *buf)
302{
303 bool ret = false;
304 int fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
305 if (fd == -1)
306 {
307 msg(M_ERRNO, "Cannot open file '%s' for write", filename);
308 return false;
309 }
310
311 const ssize_t size = write(fd, BPTR(buf), BLEN(buf));
312 if (size != BLEN(buf))
313 {
314 msg(M_ERRNO, "Write error on file '%s'", filename);
315 goto cleanup;
316 }
317
318 ret = true;
319cleanup:
320 if (close(fd) < 0)
321 {
322 msg(M_ERRNO, "Close error on file %s", filename);
323 ret = false;
324 }
325 return ret;
326}
327
328/*
329 * Garbage collection
330 */
331
332void *
333#ifdef DMALLOC
334gc_malloc_debug(size_t size, bool clear, struct gc_arena *a, const char *file, int line)
335#else
336gc_malloc(size_t size, bool clear, struct gc_arena *a)
337#endif
338{
339 void *ret;
340 if (a)
341 {
342 struct gc_entry *e;
343#ifdef DMALLOC
344 e = (struct gc_entry *)openvpn_dmalloc(file, line, size + sizeof(struct gc_entry));
345#else
346 e = (struct gc_entry *)malloc(size + sizeof(struct gc_entry));
347#endif
349 ret = (char *)e + sizeof(struct gc_entry);
350 e->next = a->list;
351 a->list = e;
352 }
353 else
354 {
355#ifdef DMALLOC
356 ret = openvpn_dmalloc(file, line, size);
357#else
358 ret = malloc(size);
359#endif
361 }
362#ifndef ZERO_BUFFER_ON_ALLOC
363 if (clear)
364#endif
365 memset(ret, 0, size);
366 return ret;
367}
368
369void *
370gc_realloc(void *ptr, size_t size, struct gc_arena *a)
371{
372 void *ret = realloc(ptr, size);
374 if (a)
375 {
376 if (ptr && ptr != ret)
377 {
378 /* find the old entry and modify it if realloc changed
379 * the pointer */
380 struct gc_entry_special *e = NULL;
381 for (e = a->list_special; e != NULL; e = e->next)
382 {
383 if (e->addr == ptr)
384 {
385 break;
386 }
387 }
388 ASSERT(e);
389 ASSERT(e->addr == ptr);
390 e->addr = ret;
391 }
392 else if (!ptr)
393 {
394 /* sets e->addr to newptr */
395 gc_addspecial(ret, free, a);
396 }
397 }
398
399 return ret;
400}
401
402void
404{
405 struct gc_entry *e;
406 e = a->list;
407 a->list = NULL;
408
409 while (e != NULL)
410 {
411 struct gc_entry *next = e->next;
412 free(e);
413 e = next;
414 }
415}
416
417/*
418 * Functions to handle special objects in gc_entries
419 */
420
421void
423{
424 struct gc_entry_special *e;
425 e = a->list_special;
426 a->list_special = NULL;
427
428 while (e != NULL)
429 {
430 struct gc_entry_special *next = e->next;
431 e->free_fnc(e->addr);
432 free(e);
433 e = next;
434 }
435}
436
437void
438gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a)
439{
440 ASSERT(a);
441 struct gc_entry_special *e;
442#ifdef DMALLOC
443 e = (struct gc_entry_special *)openvpn_dmalloc(file, line, sizeof(struct gc_entry_special));
444#else
445 e = (struct gc_entry_special *)malloc(sizeof(struct gc_entry_special));
446#endif
448 e->free_fnc = free_function;
449 e->addr = addr;
450
451 e->next = a->list_special;
452 a->list_special = e;
453}
454
455
456/*
457 * Transfer src arena to dest, resetting src to an empty arena.
458 */
459void
460gc_transfer(struct gc_arena *dest, struct gc_arena *src)
461{
462 if (dest && src)
463 {
464 struct gc_entry *e = src->list;
465 if (e)
466 {
467 while (e->next != NULL)
468 {
469 e = e->next;
470 }
471 e->next = dest->list;
472 dest->list = src->list;
473 src->list = NULL;
474 }
475 }
476}
477
478/*
479 * Hex dump -- Output a binary buffer to a hex string and return it.
480 */
481
482char *
483format_hex_ex(const uint8_t *data, int size, int maxoutput, unsigned int space_break_flags,
484 const char *separator, struct gc_arena *gc)
485{
486 const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
487 const size_t separator_len = separator ? strlen(separator) : 0;
488 static_assert(INT_MAX <= SIZE_MAX, "Code assumes INT_MAX <= SIZE_MAX");
489 const size_t out_len = maxoutput > 0
490 ? maxoutput
491 : ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
492
493 struct buffer out = alloc_buf_gc(out_len, gc);
494 for (int i = 0; i < size; ++i)
495 {
496 if (separator && i && !(i % bytes_per_hexblock))
497 {
498 buf_printf(&out, "%s", separator);
499 }
501 {
502 buf_printf(&out, "%02X", data[i]);
503 }
504 else
505 {
506 buf_printf(&out, "%02x", data[i]);
507 }
508 }
509 buf_catrunc(&out, "[more...]");
510 return (char *)out.data;
511}
512
513/*
514 * remove specific trailing character
515 */
516
517void
519{
520 uint8_t *cp = BLAST(buf);
521 if (cp && *cp == remove)
522 {
523 *cp = '\0';
524 --buf->len;
525 }
526}
527
528/*
529 * force a null termination even it requires
530 * truncation of the last char.
531 */
532void
534{
535 char *last = (char *)BLAST(buf);
536 if (last && *last == '\0') /* already terminated? */
537 {
538 return;
539 }
540
541 if (!buf_safe(buf, 1)) /* make space for trailing null */
542 {
543 buf_inc_len(buf, -1);
544 }
545
546 buf_write_u8(buf, 0);
547}
548
549/*
550 * Remove trailing \r and \n chars and ensure
551 * null termination.
552 */
553void
554buf_chomp(struct buffer *buf)
555{
556 while (true)
557 {
558 char *last = (char *)BLAST(buf);
559 if (!last)
560 {
561 break;
562 }
563 if (char_class(*last, CC_CRLF | CC_NULL))
564 {
565 if (!buf_inc_len(buf, -1))
566 {
567 break;
568 }
569 }
570 else
571 {
572 break;
573 }
574 }
576}
577
578const char *
580{
581 while (*str)
582 {
583 const char c = *str;
584 if (!(c == ' ' || c == '\t'))
585 {
586 break;
587 }
588 ++str;
589 }
590 return str;
591}
592
593/*
594 * like buf_null_terminate, but operate on strings
595 */
596void
598{
600 if (len < capacity)
601 {
602 *(str + len) = '\0';
603 }
604 else if (len == capacity)
605 {
606 *(str + len - 1) = '\0';
607 }
608}
609
610/*
611 * Remove trailing \r and \n chars.
612 */
613void
614chomp(char *str)
615{
616 rm_trailing_chars(str, "\r\n");
617}
618
619/*
620 * Remove trailing chars
621 */
622void
624{
625 bool modified;
626 do
627 {
628 const size_t len = strlen(str);
629 modified = false;
630 if (len > 0)
631 {
632 char *cp = str + (len - 1);
633 if (strchr(what_to_delete, *cp) != NULL)
634 {
635 *cp = '\0';
636 modified = true;
637 }
638 }
639 } while (modified);
640}
641
642/*
643 * Allocate a string
644 */
645char *
646#ifdef DMALLOC
647string_alloc_debug(const char *str, struct gc_arena *gc, const char *file, int line)
648#else
649string_alloc(const char *str, struct gc_arena *gc)
650#endif
651{
652 if (str)
653 {
654 const size_t n = strlen(str) + 1;
655 char *ret;
656
657 if (gc)
658 {
659#ifdef DMALLOC
660 ret = (char *)gc_malloc_debug(n, false, gc, file, line);
661#else
662 ret = (char *)gc_malloc(n, false, gc);
663#endif
664 }
665 else
666 {
667 /* If there are no garbage collector available, it's expected
668 * that the caller cleans up afterwards. This is coherent with the
669 * earlier behaviour when gc_malloc() would be called with gc == NULL
670 */
671#ifdef DMALLOC
672 ret = openvpn_dmalloc(file, line, n);
673#else
674 ret = calloc(1, n);
675#endif
677 }
678 memcpy(ret, str, n);
679 return ret;
680 }
681 else
682 {
683 return NULL;
684 }
685}
686
687/*
688 * Erase all characters in a string
689 */
690void
692{
693 if (str)
694 {
696 }
697}
698
699/*
700 * Return the length of a string array
701 */
702int
703string_array_len(const char **array)
704{
705 int i = 0;
706 if (array)
707 {
708 while (array[i])
709 {
710 ++i;
711 }
712 }
713 return i;
714}
715
716char *
717print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
718{
719 struct buffer out = alloc_buf_gc(256, gc);
720 int i = 0;
721 for (;;)
722 {
723 const char *cp = *p++;
724 if (!cp)
725 {
726 break;
727 }
728 if (i)
729 {
730 buf_printf(&out, " ");
731 }
732 if (flags & PA_BRACKET)
733 {
734 buf_printf(&out, "[%s]", cp);
735 }
736 else
737 {
738 buf_printf(&out, "%s", cp);
739 }
740 ++i;
741 }
742 return BSTR(&out);
743}
744
745/*
746 * Allocate a string inside a buffer
747 */
748struct buffer
750string_alloc_buf_debug(const char *str, struct gc_arena *gc, const char *file, int line)
751#else
752string_alloc_buf(const char *str, struct gc_arena *gc)
753#endif
754{
755 struct buffer buf;
756
757 ASSERT(str);
758
759#ifdef DMALLOC
760 buf_set_read(&buf, (uint8_t *)string_alloc_debug(str, gc, file, line), strlen(str) + 1);
761#else
762 buf_set_read(&buf, (uint8_t *)string_alloc(str, gc), strlen(str) + 1);
763#endif
764
765 if (buf.len > 0) /* Don't count trailing '\0' as part of length */
766 {
767 --buf.len;
768 }
769
770 return buf;
771}
772
773/*
774 * String comparison
775 */
776
777bool
778buf_string_match_head_str(const struct buffer *src, const char *match)
779{
780 const size_t size = strlen(match);
781 if (size > src->len)
782 {
783 return false;
784 }
785 return memcmp(BPTR(src), match, size) == 0;
786}
787
788bool
790{
792 {
793 buf_advance(src, (int)strlen(match));
794 return true;
795 }
796 else
797 {
798 return false;
799 }
800}
801
802int
803buf_substring_len(const struct buffer *buf, int delim)
804{
805 int i = 0;
806 struct buffer tmp = *buf;
807 int c;
808
809 while ((c = buf_read_u8(&tmp)) >= 0)
810 {
811 ++i;
812 if (c == delim)
813 {
814 return i;
815 }
816 }
817 return -1;
818}
819
820/*
821 * String parsing
822 */
823
824bool
825buf_parse(struct buffer *buf, const int delim, char *line, const int size)
826{
827 bool eol = false;
828 int n = 0;
829 int c;
830
831 ASSERT(size > 0);
832
833 do
834 {
835 c = buf_peek_u8(buf);
836 if (c < 0)
837 {
838 eol = true;
839 line[n] = 0;
840 break;
841 }
842 if (c == delim)
843 {
844 buf_advance(buf, 1);
845 line[n] = 0;
846 break;
847 }
848 if (n >= (size - 1))
849 {
850 break;
851 }
852 buf_advance(buf, 1);
853 line[n++] = (char)c;
854 } while (c);
855
856 line[size - 1] = '\0';
857 return !(eol && !strlen(line));
858}
859
860/*
861 * Print a string which might be NULL
862 */
863const char *
864np(const char *str)
865{
866 if (str)
867 {
868 return str;
869 }
870 else
871 {
872 return "[NULL]";
873 }
874}
875
876/*
877 * Classify and mutate strings based on character types.
878 */
879
880bool
881char_class(const unsigned char c, const unsigned int flags)
882{
883 if (!flags)
884 {
885 return false;
886 }
887 if (flags & CC_ANY)
888 {
889 return true;
890 }
891
892 if ((flags & CC_NULL) && c == '\0')
893 {
894 return true;
895 }
896
897 if ((flags & CC_ALNUM) && isalnum(c))
898 {
899 return true;
900 }
901 if ((flags & CC_ALPHA) && isalpha(c))
902 {
903 return true;
904 }
905 if ((flags & CC_ASCII) && isascii(c))
906 {
907 return true;
908 }
909 if ((flags & CC_CNTRL) && iscntrl(c))
910 {
911 return true;
912 }
913 if ((flags & CC_DIGIT) && isdigit(c))
914 {
915 return true;
916 }
917 /* allow ascii non-control and UTF-8, consider DEL to be a control */
918 if ((flags & CC_PRINT) && (c >= 32 && c != 127))
919 {
920 return true;
921 }
922 if ((flags & CC_PUNCT) && ispunct(c))
923 {
924 return true;
925 }
926 if ((flags & CC_SPACE) && isspace(c))
927 {
928 return true;
929 }
930 if ((flags & CC_XDIGIT) && isxdigit(c))
931 {
932 return true;
933 }
934
935 if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
936 {
937 return true;
938 }
939 if ((flags & CC_NEWLINE) && c == '\n')
940 {
941 return true;
942 }
943 if ((flags & CC_CR) && c == '\r')
944 {
945 return true;
946 }
947
948 if ((flags & CC_BACKSLASH) && c == '\\')
949 {
950 return true;
951 }
952 if ((flags & CC_UNDERBAR) && c == '_')
953 {
954 return true;
955 }
956 if ((flags & CC_DASH) && c == '-')
957 {
958 return true;
959 }
960 if ((flags & CC_DOT) && c == '.')
961 {
962 return true;
963 }
964 if ((flags & CC_COMMA) && c == ',')
965 {
966 return true;
967 }
968 if ((flags & CC_COLON) && c == ':')
969 {
970 return true;
971 }
972 if ((flags & CC_SLASH) && c == '/')
973 {
974 return true;
975 }
976 if ((flags & CC_SINGLE_QUOTE) && c == '\'')
977 {
978 return true;
979 }
980 if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
981 {
982 return true;
983 }
984 if ((flags & CC_REVERSE_QUOTE) && c == '`')
985 {
986 return true;
987 }
988 if ((flags & CC_AT) && c == '@')
989 {
990 return true;
991 }
992 if ((flags & CC_EQUAL) && c == '=')
993 {
994 return true;
995 }
996 if ((flags & CC_LESS_THAN) && c == '<')
997 {
998 return true;
999 }
1000 if ((flags & CC_GREATER_THAN) && c == '>')
1001 {
1002 return true;
1003 }
1004 if ((flags & CC_PIPE) && c == '|')
1005 {
1006 return true;
1007 }
1008 if ((flags & CC_QUESTION_MARK) && c == '?')
1009 {
1010 return true;
1011 }
1012 if ((flags & CC_ASTERISK) && c == '*')
1013 {
1014 return true;
1015 }
1016
1017 return false;
1018}
1019
1020static inline bool
1021char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
1022{
1023 return char_class(c, inclusive) && !char_class(c, exclusive);
1024}
1025
1026bool
1027string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
1028{
1029 char c;
1030 ASSERT(str);
1031 while ((c = *str++))
1032 {
1034 {
1035 return false;
1036 }
1037 }
1038 return true;
1039}
1040
1041/*
1042 * Modify string in place.
1043 * Guaranteed to not increase string length.
1044 */
1045bool
1046string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive,
1047 const char replace)
1048{
1049 const char *in = str;
1050 bool ret = true;
1051
1052 ASSERT(str);
1053
1054 while (true)
1055 {
1056 char c = *in++;
1057 if (c)
1058 {
1060 {
1061 c = replace;
1062 ret = false;
1063 }
1064 if (c)
1065 {
1066 *str++ = c;
1067 }
1068 }
1069 else
1070 {
1071 *str = '\0';
1072 break;
1073 }
1074 }
1075 return ret;
1076}
1077
1078bool
1079string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
1080{
1081 ASSERT(buf);
1082
1083 for (int i = 0; i < BLEN(buf); i++)
1084 {
1085 char c = BSTR(buf)[i];
1086
1088 {
1089 return false;
1090 }
1091 }
1092 return true;
1093}
1094
1095const char *
1096string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive,
1097 const char replace, struct gc_arena *gc)
1098{
1099 if (str)
1100 {
1101 char *buf = string_alloc(str, gc);
1103 return buf;
1104 }
1105 else
1106 {
1107 return NULL;
1108 }
1109}
1110
1111void
1112string_replace_leading(char *str, const char match, const char replace)
1113{
1114 ASSERT(match != '\0');
1115 while (*str)
1116 {
1117 if (*str == match)
1118 {
1119 *str = replace;
1120 }
1121 else
1122 {
1123 break;
1124 }
1125 ++str;
1126 }
1127}
1128
1129#ifdef VERIFY_ALIGNMENT
1130void
1131valign4(const struct buffer *buf, const char *file, const int line)
1132{
1133 if (buf && buf->len)
1134 {
1135 msglvl_t msglevel = D_ALIGN_DEBUG;
1136 const unsigned int u = (unsigned int)BPTR(buf);
1137
1138 if (u & (PAYLOAD_ALIGN - 1))
1139 {
1140 msglevel = D_ALIGN_ERRORS;
1141 }
1142
1143 msg(msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
1144 (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "", file, line, (ptr_type)buf->data,
1145 buf->offset, buf->len, buf->capacity, buf_debug_file(buf), buf_debug_line(buf));
1146 }
1147}
1148#endif /* ifdef VERIFY_ALIGNMENT */
1149
1150/*
1151 * struct buffer_list
1152 */
1153struct buffer_list *
1155{
1156 struct buffer_list *ret;
1157 ALLOC_OBJ_CLEAR(ret, struct buffer_list);
1158 ret->size = 0;
1159 return ret;
1160}
1161
1162void
1164{
1165 if (ol)
1166 {
1168 free(ol);
1169 }
1170}
1171
1172bool
1174{
1175 return ol && ol->head != NULL;
1176}
1177
1178void
1180{
1181 struct buffer_entry *e = ol->head;
1182 while (e)
1183 {
1184 struct buffer_entry *next = e->next;
1185 free_buf(&e->buf);
1186 free(e);
1187 e = next;
1188 }
1189 ol->head = ol->tail = NULL;
1190 ol->size = 0;
1191}
1192
1193void
1194buffer_list_push(struct buffer_list *ol, const char *str)
1195{
1196 if (str)
1197 {
1198 const size_t len = strlen((const char *)str);
1199 struct buffer_entry *e = buffer_list_push_data(ol, str, len + 1);
1200 if (e)
1201 {
1202 e->buf.len = (int)len; /* Don't count trailing '\0' as part of length */
1203 }
1204 }
1205}
1206
1207struct buffer_entry *
1208buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
1209{
1210 struct buffer_entry *e = NULL;
1211 if (data)
1212 {
1213 ALLOC_OBJ_CLEAR(e, struct buffer_entry);
1214
1215 ++ol->size;
1216 if (ol->tail)
1217 {
1218 ASSERT(ol->head);
1219 ol->tail->next = e;
1220 }
1221 else
1222 {
1223 ASSERT(!ol->head);
1224 ol->head = e;
1225 }
1226 e->buf = alloc_buf(size);
1227 memcpy(e->buf.data, data, size);
1228 e->buf.len = (int)size;
1229 ol->tail = e;
1230 }
1231 return e;
1232}
1233
1234struct buffer *
1236{
1237 if (ol && ol->head)
1238 {
1239 return &ol->head->buf;
1240 }
1241 else
1242 {
1243 return NULL;
1244 }
1245}
1246
1247void
1248buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
1249{
1250 const size_t sep_len = strlen(sep);
1251 struct buffer_entry *more = bl->head;
1252 size_t size = 0;
1253 int count = 0;
1254 for (count = 0; more; ++count)
1255 {
1256 size_t extra_len = BLEN(&more->buf) + sep_len;
1257 if (size + extra_len > max_len)
1258 {
1259 break;
1260 }
1261
1262 size += extra_len;
1263 more = more->next;
1264 }
1265
1266 if (count >= 2)
1267 {
1268 struct buffer_entry *f;
1269 ALLOC_OBJ_CLEAR(f, struct buffer_entry);
1270 f->buf = alloc_buf(size + 1); /* prevent 0-byte malloc */
1271
1272 struct buffer_entry *e = bl->head;
1273 for (size_t i = 0; e && i < count; ++i)
1274 {
1275 struct buffer_entry *next = e->next;
1276 buf_copy(&f->buf, &e->buf);
1277 buf_write(&f->buf, sep, sep_len);
1278 free_buf(&e->buf);
1279 free(e);
1280 e = next;
1281 }
1282 bl->head = f;
1283 bl->size -= count - 1;
1284 f->next = more;
1285 if (!more)
1286 {
1287 bl->tail = f;
1288 }
1289 }
1290}
1291
1292void
1293buffer_list_aggregate(struct buffer_list *bl, const size_t max)
1294{
1296}
1297
1298void
1300{
1301 if (ol && ol->head)
1302 {
1303 struct buffer_entry *e = ol->head->next;
1304 free_buf(&ol->head->buf);
1305 free(ol->head);
1306 ol->head = e;
1307 --ol->size;
1308 if (!e)
1309 {
1310 ol->tail = NULL;
1311 }
1312 }
1313}
1314
1315void
1317{
1318 if (ol->head)
1319 {
1320 struct buffer *buf = &ol->head->buf;
1321 ASSERT(buf_advance(buf, n));
1322 if (!BLEN(buf))
1323 {
1325 }
1326 }
1327}
1328
1329struct buffer_list *
1330buffer_list_file(const char *fn, int max_line_len)
1331{
1332 FILE *fp = platform_fopen(fn, "r");
1333 struct buffer_list *bl = NULL;
1334
1335 if (fp)
1336 {
1337 char *line = (char *)malloc(max_line_len);
1338 if (line)
1339 {
1340 bl = buffer_list_new();
1341 while (fgets(line, max_line_len, fp) != NULL)
1342 {
1343 buffer_list_push(bl, line);
1344 }
1345 free(line);
1346 }
1347 fclose(fp);
1348 }
1349 return bl;
1350}
1351
1352struct buffer
1354{
1355 struct buffer ret = { 0 };
1356
1357 platform_stat_t file_stat = { 0 };
1358 if (platform_stat(filename, &file_stat) < 0)
1359 {
1360 return ret;
1361 }
1362
1363 FILE *fp = platform_fopen(filename, "r");
1364 if (!fp)
1365 {
1366 return ret;
1367 }
1368
1369 const size_t size = file_stat.st_size;
1370 ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */
1371 size_t read_size = fread(BPTR(&ret), 1, size, fp);
1372 if (read_size == 0)
1373 {
1374 free_buf_gc(&ret, gc);
1375 goto cleanup;
1376 }
1377 ASSERT(buf_inc_len(&ret, (int)read_size));
1378 buf_null_terminate(&ret);
1379
1380cleanup:
1381 fclose(fp);
1382 return ret;
1383}
bool buffer_list_defined(const struct buffer_list *ol)
Checks if the list is valid and non-empty.
Definition buffer.c:1173
bool buf_string_compare_advance(struct buffer *src, const char *match)
Definition buffer.c:789
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:1208
void rm_trailing_chars(char *str, const char *what_to_delete)
Definition buffer.c:623
void string_null_terminate(char *str, int len, int capacity)
Definition buffer.c:597
void buffer_list_advance(struct buffer_list *ol, int n)
Definition buffer.c:1316
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:1248
static bool char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
Definition buffer.c:1021
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:1179
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:579
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:1293
struct buffer clone_buf(const struct buffer *buf)
Definition buffer.c:116
void x_gc_freespecial(struct gc_arena *a)
Definition buffer.c:422
bool buffer_write_file(const char *filename, const struct buffer *buf)
Write buffer contents to file.
Definition buffer.c:301
void buf_catrunc(struct buffer *buf, const char *str)
Definition buffer.c:288
#define buf_debug_file(buf)
Definition buffer.c:158
void buffer_list_pop(struct buffer_list *ol)
Definition buffer.c:1299
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:1112
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:1330
void string_clear(char *str)
Definition buffer.c:691
#define buf_debug_line(buf)
Definition buffer.c:157
void gc_transfer(struct gc_arena *dest, struct gc_arena *src)
Definition buffer.c:460
bool string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
Definition buffer.c:1027
char * print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
Definition buffer.c:717
void buf_null_terminate(struct buffer *buf)
Definition buffer.c:533
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:370
char * format_hex_ex(const uint8_t *data, int size, int maxoutput, unsigned int space_break_flags, const char *separator, struct gc_arena *gc)
Definition buffer.c:483
struct buffer_list * buffer_list_new(void)
Allocate an empty buffer list of capacity max_size.
Definition buffer.c:1154
void chomp(char *str)
Definition buffer.c:614
struct buffer * buffer_list_peek(struct buffer_list *ol)
Retrieve the head buffer.
Definition buffer.c:1235
const char * np(const char *str)
Definition buffer.c:864
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:1079
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:1163
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition buffer.c:336
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
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:1046
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:1096
void gc_addspecial(void *addr, void(*free_function)(void *), struct gc_arena *a)
Definition buffer.c:438
int string_array_len(const char **array)
Definition buffer.c:703
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:1353
void buf_rmtail(struct buffer *buf, uint8_t remove)
Definition buffer.c:518
bool buf_parse(struct buffer *buf, const int delim, char *line, const int size)
Definition buffer.c:825
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:649
struct buffer string_alloc_buf(const char *str, struct gc_arena *gc)
Definition buffer.c:752
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:1194
void x_gc_free(struct gc_arena *a)
Definition buffer.c:403
void buf_chomp(struct buffer *buf)
Definition buffer.c:554
bool char_class(const unsigned char c, const unsigned int flags)
Definition buffer.c:881
bool buf_string_match_head_str(const struct buffer *src, const char *match)
Definition buffer.c:778
int buf_substring_len(const struct buffer *buf, int delim)
Definition buffer.c:803
#define CC_COMMA
comma
Definition buffer.h:898
#define BEND(buf)
Definition buffer.h:124
#define CC_DASH
dash
Definition buffer.h:896
static bool buf_size_valid(const size_t size)
Definition buffer.h:285
#define CC_DOUBLE_QUOTE
double quote
Definition buffer.h:902
#define BLAST(buf)
Definition buffer.h:125
#define CC_BLANK
space or tab
Definition buffer.h:890
static bool buf_init_dowork(struct buffer *buf, int offset)
Definition buffer.h:319
#define CC_PIPE
pipe
Definition buffer.h:908
#define BSTR(buf)
Definition buffer.h:128
static bool buf_copy(struct buffer *dest, const struct buffer *src)
Definition buffer.h:704
#define CC_ANY
any character
Definition buffer.h:877
#define BPTR(buf)
Definition buffer.h:123
#define CC_AT
at sign
Definition buffer.h:904
static int buf_peek_u8(struct buffer *buf)
Definition buffer.h:774
#define CC_XDIGIT
hex digit isxdigit()
Definition buffer.h:888
#define CC_COLON
colon
Definition buffer.h:899
#define CC_SINGLE_QUOTE
single quote
Definition buffer.h:901
static bool buf_inc_len(struct buffer *buf, int inc)
Definition buffer.h:588
#define CC_DIGIT
digit isdigit()
Definition buffer.h:884
#define PA_BRACKET
Definition buffer.h:143
#define CC_DOT
dot
Definition buffer.h:897
#define CC_CRLF
carriage return or newline
Definition buffer.h:914
static bool buf_safe(const struct buffer *buf, size_t len)
Definition buffer.h:518
#define CC_ASTERISK
asterisk
Definition buffer.h:910
#define CC_ALPHA
alphabetic isalpha()
Definition buffer.h:881
static bool buf_advance(struct buffer *buf, int size)
Definition buffer.h:616
#define CC_NEWLINE
newline
Definition buffer.h:891
static void buf_set_read(struct buffer *buf, const uint8_t *data, size_t size)
Definition buffer.h:348
static int buf_forward_capacity(const struct buffer *buf)
Definition buffer.h:539
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:414
static uint8_t * buf_write_alloc(struct buffer *buf, size_t size)
Definition buffer.h:633
#define CC_REVERSE_QUOTE
reverse quote
Definition buffer.h:903
#define CC_SPACE
whitespace isspace()
Definition buffer.h:887
#define CC_ASCII
ASCII character.
Definition buffer.h:882
static bool buf_write(struct buffer *dest, const void *src, size_t size)
Definition buffer.h:660
static uint8_t * buf_prepend(struct buffer *buf, int size)
Definition buffer.h:604
#define CC_BACKSLASH
backslash
Definition buffer.h:894
#define CC_CNTRL
control character iscntrl()
Definition buffer.h:883
#define CC_LESS_THAN
less than sign
Definition buffer.h:906
static bool buf_write_u8(struct buffer *dest, uint8_t data)
Definition buffer.h:684
static int buf_read_u8(struct buffer *buf)
Definition buffer.h:786
#define BLEN(buf)
Definition buffer.h:126
#define CC_CR
carriage return
Definition buffer.h:892
#define CC_SLASH
slash
Definition buffer.h:900
#define CC_UNDERBAR
underscore
Definition buffer.h:895
#define CC_GREATER_THAN
greater than sign
Definition buffer.h:907
static void strncpynt(char *dest, const char *src, size_t maxlen)
Definition buffer.h:361
static void check_malloc_return(void *p)
Definition buffer.h:1095
#define CC_NULL
null character \0
Definition buffer.h:878
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:885
#define CC_QUESTION_MARK
question mark
Definition buffer.h:909
#define ALLOC_OBJ_CLEAR(dptr, type)
Definition buffer.h:1052
static bool buf_defined(const struct buffer *buf)
Definition buffer.h:228
#define CC_ALNUM
alphanumeric isalnum()
Definition buffer.h:880
#define buf_init(buf, offset)
Definition buffer.h:209
#define CC_EQUAL
equal sign
Definition buffer.h:905
#define FHE_SPACE_BREAK_MASK
Definition buffer.h:497
#define CC_PUNCT
punctuation ispunct()
Definition buffer.h:886
#define FHE_CAPS
Definition buffer.h:498
static int buf_forward_capacity_total(const struct buffer *buf)
Definition buffer.h:557
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:1107
struct buffer_entry * next
Definition buffer.h:1109
struct buffer buf
Definition buffer.h:1108
struct buffer_entry * tail
Definition buffer.h:1115
struct buffer_entry * head
Definition buffer.h:1114
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)
char ** res
struct gc_arena gc
Definition test_ssl.c:131