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-2022 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, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #elif defined(_MSC_VER)
27 #include "config-msvc.h"
28 #endif
29
30 #include "syshead.h"
31
32 #include "common.h"
33 #include "buffer.h"
34 #include "error.h"
35 #include "mtu.h"
36 #include "misc.h"
37
38 #include "memdbg.h"
39
40 #include <wchar.h>
41
42 size_t
array_mult_safe(const size_t m1,const size_t m2,const size_t extra)43 array_mult_safe(const size_t m1, const size_t m2, const size_t extra)
44 {
45 const size_t limit = 0xFFFFFFFF;
46 unsigned long long res = (unsigned long long)m1 * (unsigned long long)m2 + (unsigned long long)extra;
47 if (unlikely(m1 > limit) || unlikely(m2 > limit) || unlikely(extra > limit) || unlikely(res > (unsigned long long)limit))
48 {
49 msg(M_FATAL, "attempted allocation of excessively large array");
50 }
51 return (size_t) res;
52 }
53
54 void
buf_size_error(const size_t size)55 buf_size_error(const size_t size)
56 {
57 msg(M_FATAL, "fatal buffer size error, size=%lu", (unsigned long)size);
58 }
59
60 struct buffer
61 #ifdef DMALLOC
alloc_buf_debug(size_t size,const char * file,int line)62 alloc_buf_debug(size_t size, const char *file, int line)
63 #else
64 alloc_buf(size_t size)
65 #endif
66 {
67 struct buffer buf;
68
69 if (!buf_size_valid(size))
70 {
71 buf_size_error(size);
72 }
73 buf.capacity = (int)size;
74 buf.offset = 0;
75 buf.len = 0;
76 #ifdef DMALLOC
77 buf.data = openvpn_dmalloc(file, line, size);
78 #else
79 buf.data = calloc(1, size);
80 #endif
81 check_malloc_return(buf.data);
82
83 return buf;
84 }
85
86 struct buffer
87 #ifdef DMALLOC
alloc_buf_gc_debug(size_t size,struct gc_arena * gc,const char * file,int line)88 alloc_buf_gc_debug(size_t size, struct gc_arena *gc, const char *file, int line)
89 #else
90 alloc_buf_gc(size_t size, struct gc_arena *gc)
91 #endif
92 {
93 struct buffer buf;
94 if (!buf_size_valid(size))
95 {
96 buf_size_error(size);
97 }
98 buf.capacity = (int)size;
99 buf.offset = 0;
100 buf.len = 0;
101 #ifdef DMALLOC
102 buf.data = (uint8_t *) gc_malloc_debug(size, false, gc, file, line);
103 #else
104 buf.data = (uint8_t *) gc_malloc(size, false, gc);
105 #endif
106 if (size)
107 {
108 *buf.data = 0;
109 }
110 return buf;
111 }
112
113 struct buffer
114 #ifdef DMALLOC
clone_buf_debug(const struct buffer * buf,const char * file,int line)115 clone_buf_debug(const struct buffer *buf, const char *file, int line)
116 #else
117 clone_buf(const struct buffer *buf)
118 #endif
119 {
120 struct buffer ret;
121 ret.capacity = buf->capacity;
122 ret.offset = buf->offset;
123 ret.len = buf->len;
124 #ifdef DMALLOC
125 ret.data = (uint8_t *) openvpn_dmalloc(file, line, buf->capacity);
126 #else
127 ret.data = (uint8_t *) malloc(buf->capacity);
128 #endif
129 check_malloc_return(ret.data);
130 memcpy(BPTR(&ret), BPTR(buf), BLEN(buf));
131 return ret;
132 }
133
134 #ifdef BUF_INIT_TRACKING
135
136 bool
buf_init_debug(struct buffer * buf,int offset,const char * file,int line)137 buf_init_debug(struct buffer *buf, int offset, const char *file, int line)
138 {
139 buf->debug_file = file;
140 buf->debug_line = line;
141 return buf_init_dowork(buf, offset);
142 }
143
144 static inline int
buf_debug_line(const struct buffer * buf)145 buf_debug_line(const struct buffer *buf)
146 {
147 return buf->debug_line;
148 }
149
150 static const char *
buf_debug_file(const struct buffer * buf)151 buf_debug_file(const struct buffer *buf)
152 {
153 return buf->debug_file;
154 }
155
156 #else /* ifdef BUF_INIT_TRACKING */
157
158 #define buf_debug_line(buf) 0
159 #define buf_debug_file(buf) "[UNDEF]"
160
161 #endif /* ifdef BUF_INIT_TRACKING */
162
163 void
buf_clear(struct buffer * buf)164 buf_clear(struct buffer *buf)
165 {
166 if (buf->capacity > 0)
167 {
168 secure_memzero(buf->data, buf->capacity);
169 }
170 buf->len = 0;
171 buf->offset = 0;
172 }
173
174 bool
buf_assign(struct buffer * dest,const struct buffer * src)175 buf_assign(struct buffer *dest, const struct buffer *src)
176 {
177 if (!buf_init(dest, src->offset))
178 {
179 return false;
180 }
181 return buf_write(dest, BPTR(src), BLEN(src));
182 }
183
184 void
free_buf(struct buffer * buf)185 free_buf(struct buffer *buf)
186 {
187 if (buf->data)
188 {
189 free(buf->data);
190 }
191 CLEAR(*buf);
192 }
193
194 static void
free_buf_gc(struct buffer * buf,struct gc_arena * gc)195 free_buf_gc(struct buffer *buf, struct gc_arena *gc)
196 {
197 if (gc)
198 {
199 struct gc_entry **e = &gc->list;
200
201 while (*e)
202 {
203 /* check if this object is the one we want to delete */
204 if ((uint8_t *)(*e + 1) == buf->data)
205 {
206 struct gc_entry *to_delete = *e;
207
208 /* remove element from linked list and free it */
209 *e = (*e)->next;
210 free(to_delete);
211
212 break;
213 }
214
215 e = &(*e)->next;
216 }
217 }
218
219 CLEAR(*buf);
220 }
221
222 /*
223 * Return a buffer for write that is a subset of another buffer
224 */
225 struct buffer
buf_sub(struct buffer * buf,int size,bool prepend)226 buf_sub(struct buffer *buf, int size, bool prepend)
227 {
228 struct buffer ret;
229 uint8_t *data;
230
231 CLEAR(ret);
232 data = prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size);
233 if (data)
234 {
235 ret.capacity = size;
236 ret.data = data;
237 }
238 return ret;
239 }
240
241 /*
242 * printf append to a buffer with overflow check
243 */
244 bool
buf_printf(struct buffer * buf,const char * format,...)245 buf_printf(struct buffer *buf, const char *format, ...)
246 {
247 int ret = false;
248 if (buf_defined(buf))
249 {
250 va_list arglist;
251 uint8_t *ptr = BEND(buf);
252 int cap = buf_forward_capacity(buf);
253
254 if (cap > 0)
255 {
256 int stat;
257 va_start(arglist, format);
258 stat = vsnprintf((char *)ptr, cap, format, arglist);
259 va_end(arglist);
260 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
261 buf->len += (int) strlen((char *)ptr);
262 if (stat >= 0 && stat < cap)
263 {
264 ret = true;
265 }
266 }
267 }
268 return ret;
269 }
270
271 bool
buf_puts(struct buffer * buf,const char * str)272 buf_puts(struct buffer *buf, const char *str)
273 {
274 int ret = false;
275 uint8_t *ptr = BEND(buf);
276 int cap = buf_forward_capacity(buf);
277 if (cap > 0)
278 {
279 strncpynt((char *)ptr,str, cap);
280 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
281 buf->len += (int) strlen((char *)ptr);
282 ret = true;
283 }
284 return ret;
285 }
286
287
288 /*
289 * This is necessary due to certain buggy implementations of snprintf,
290 * that don't guarantee null termination for size > 0.
291 *
292 * Return false on overflow.
293 *
294 * This functionality is duplicated in src/openvpnserv/common.c
295 * Any modifications here should be done to the other place as well.
296 */
297
298 bool
openvpn_snprintf(char * str,size_t size,const char * format,...)299 openvpn_snprintf(char *str, size_t size, const char *format, ...)
300 {
301 va_list arglist;
302 int len = -1;
303 if (size > 0)
304 {
305 va_start(arglist, format);
306 len = vsnprintf(str, size, format, arglist);
307 va_end(arglist);
308 str[size - 1] = 0;
309 }
310 return (len >= 0 && len < size);
311 }
312
313 /*
314 * write a string to the end of a buffer that was
315 * truncated by buf_printf
316 */
317 void
buf_catrunc(struct buffer * buf,const char * str)318 buf_catrunc(struct buffer *buf, const char *str)
319 {
320 if (buf_forward_capacity(buf) <= 1)
321 {
322 int len = (int) strlen(str) + 1;
323 if (len < buf_forward_capacity_total(buf))
324 {
325 strncpynt((char *)(buf->data + buf->capacity - len), str, len);
326 }
327 }
328 }
329
330 /*
331 * convert a multi-line output to one line
332 */
333 void
convert_to_one_line(struct buffer * buf)334 convert_to_one_line(struct buffer *buf)
335 {
336 uint8_t *cp = BPTR(buf);
337 int len = BLEN(buf);
338 while (len--)
339 {
340 if (*cp == '\n')
341 {
342 *cp = '|';
343 }
344 ++cp;
345 }
346 }
347
348 bool
buffer_write_file(const char * filename,const struct buffer * buf)349 buffer_write_file(const char *filename, const struct buffer *buf)
350 {
351 bool ret = false;
352 int fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY,
353 S_IRUSR | S_IWUSR);
354 if (fd == -1)
355 {
356 msg(M_ERRNO, "Cannot open file '%s' for write", filename);
357 return false;
358 }
359
360 const int size = write(fd, BPTR(buf), BLEN(buf));
361 if (size != BLEN(buf))
362 {
363 msg(M_ERRNO, "Write error on file '%s'", filename);
364 goto cleanup;
365 }
366
367 ret = true;
368 cleanup:
369 if (close(fd) < 0)
370 {
371 msg(M_ERRNO, "Close error on file %s", filename);
372 ret = false;
373 }
374 return ret;
375 }
376
377 /*
378 * Garbage collection
379 */
380
381 void *
382 #ifdef DMALLOC
gc_malloc_debug(size_t size,bool clear,struct gc_arena * a,const char * file,int line)383 gc_malloc_debug(size_t size, bool clear, struct gc_arena *a, const char *file, int line)
384 #else
385 gc_malloc(size_t size, bool clear, struct gc_arena *a)
386 #endif
387 {
388 void *ret;
389 if (a)
390 {
391 struct gc_entry *e;
392 #ifdef DMALLOC
393 e = (struct gc_entry *) openvpn_dmalloc(file, line, size + sizeof(struct gc_entry));
394 #else
395 e = (struct gc_entry *) malloc(size + sizeof(struct gc_entry));
396 #endif
397 check_malloc_return(e);
398 ret = (char *) e + sizeof(struct gc_entry);
399 e->next = a->list;
400 a->list = e;
401 }
402 else
403 {
404 #ifdef DMALLOC
405 ret = openvpn_dmalloc(file, line, size);
406 #else
407 ret = malloc(size);
408 #endif
409 check_malloc_return(ret);
410 }
411 #ifndef ZERO_BUFFER_ON_ALLOC
412 if (clear)
413 #endif
414 memset(ret, 0, size);
415 return ret;
416 }
417
418 void
x_gc_free(struct gc_arena * a)419 x_gc_free(struct gc_arena *a)
420 {
421 struct gc_entry *e;
422 e = a->list;
423 a->list = NULL;
424
425 while (e != NULL)
426 {
427 struct gc_entry *next = e->next;
428 free(e);
429 e = next;
430 }
431 }
432
433 /*
434 * Functions to handle special objects in gc_entries
435 */
436
437 void
x_gc_freespecial(struct gc_arena * a)438 x_gc_freespecial(struct gc_arena *a)
439 {
440 struct gc_entry_special *e;
441 e = a->list_special;
442 a->list_special = NULL;
443
444 while (e != NULL)
445 {
446 struct gc_entry_special *next = e->next;
447 e->free_fnc(e->addr);
448 free(e);
449 e = next;
450 }
451 }
452
453 void
gc_addspecial(void * addr,void (* free_function)(void *),struct gc_arena * a)454 gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a)
455 {
456 ASSERT(a);
457 struct gc_entry_special *e;
458 #ifdef DMALLOC
459 e = (struct gc_entry_special *) openvpn_dmalloc(file, line, sizeof(struct gc_entry_special));
460 #else
461 e = (struct gc_entry_special *) malloc(sizeof(struct gc_entry_special));
462 #endif
463 check_malloc_return(e);
464 e->free_fnc = free_function;
465 e->addr = addr;
466
467 e->next = a->list_special;
468 a->list_special = e;
469 }
470
471
472 /*
473 * Transfer src arena to dest, resetting src to an empty arena.
474 */
475 void
gc_transfer(struct gc_arena * dest,struct gc_arena * src)476 gc_transfer(struct gc_arena *dest, struct gc_arena *src)
477 {
478 if (dest && src)
479 {
480 struct gc_entry *e = src->list;
481 if (e)
482 {
483 while (e->next != NULL)
484 {
485 e = e->next;
486 }
487 e->next = dest->list;
488 dest->list = src->list;
489 src->list = NULL;
490 }
491 }
492 }
493
494 /*
495 * Hex dump -- Output a binary buffer to a hex string and return it.
496 */
497
498 char *
format_hex_ex(const uint8_t * data,int size,int maxoutput,unsigned int space_break_flags,const char * separator,struct gc_arena * gc)499 format_hex_ex(const uint8_t *data, int size, int maxoutput,
500 unsigned int space_break_flags, const char *separator,
501 struct gc_arena *gc)
502 {
503 const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
504 const size_t separator_len = separator ? strlen(separator) : 0;
505 static_assert(INT_MAX <= SIZE_MAX, "Code assumes INT_MAX <= SIZE_MAX");
506 const size_t out_len = maxoutput > 0 ? maxoutput :
507 ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
508
509 struct buffer out = alloc_buf_gc(out_len, gc);
510 for (int i = 0; i < size; ++i)
511 {
512 if (separator && i && !(i % bytes_per_hexblock))
513 {
514 buf_printf(&out, "%s", separator);
515 }
516 if (space_break_flags & FHE_CAPS)
517 {
518 buf_printf(&out, "%02X", data[i]);
519 }
520 else
521 {
522 buf_printf(&out, "%02x", data[i]);
523 }
524 }
525 buf_catrunc(&out, "[more...]");
526 return (char *)out.data;
527 }
528
529 /*
530 * remove specific trailing character
531 */
532
533 void
buf_rmtail(struct buffer * buf,uint8_t remove)534 buf_rmtail(struct buffer *buf, uint8_t remove)
535 {
536 uint8_t *cp = BLAST(buf);
537 if (cp && *cp == remove)
538 {
539 *cp = '\0';
540 --buf->len;
541 }
542 }
543
544 /*
545 * force a null termination even it requires
546 * truncation of the last char.
547 */
548 void
buf_null_terminate(struct buffer * buf)549 buf_null_terminate(struct buffer *buf)
550 {
551 char *last = (char *) BLAST(buf);
552 if (last && *last == '\0') /* already terminated? */
553 {
554 return;
555 }
556
557 if (!buf_safe(buf, 1)) /* make space for trailing null */
558 {
559 buf_inc_len(buf, -1);
560 }
561
562 buf_write_u8(buf, 0);
563 }
564
565 /*
566 * Remove trailing \r and \n chars and ensure
567 * null termination.
568 */
569 void
buf_chomp(struct buffer * buf)570 buf_chomp(struct buffer *buf)
571 {
572 while (true)
573 {
574 char *last = (char *) BLAST(buf);
575 if (!last)
576 {
577 break;
578 }
579 if (char_class(*last, CC_CRLF|CC_NULL))
580 {
581 if (!buf_inc_len(buf, -1))
582 {
583 break;
584 }
585 }
586 else
587 {
588 break;
589 }
590 }
591 buf_null_terminate(buf);
592 }
593
594 const char *
skip_leading_whitespace(const char * str)595 skip_leading_whitespace(const char *str)
596 {
597 while (*str)
598 {
599 const char c = *str;
600 if (!(c == ' ' || c == '\t'))
601 {
602 break;
603 }
604 ++str;
605 }
606 return str;
607 }
608
609 /*
610 * like buf_null_terminate, but operate on strings
611 */
612 void
string_null_terminate(char * str,int len,int capacity)613 string_null_terminate(char *str, int len, int capacity)
614 {
615 ASSERT(len >= 0 && len <= capacity && capacity > 0);
616 if (len < capacity)
617 {
618 *(str + len) = '\0';
619 }
620 else if (len == capacity)
621 {
622 *(str + len - 1) = '\0';
623 }
624 }
625
626 /*
627 * Remove trailing \r and \n chars.
628 */
629 void
chomp(char * str)630 chomp(char *str)
631 {
632 rm_trailing_chars(str, "\r\n");
633 }
634
635 /*
636 * Remove trailing chars
637 */
638 void
rm_trailing_chars(char * str,const char * what_to_delete)639 rm_trailing_chars(char *str, const char *what_to_delete)
640 {
641 bool modified;
642 do
643 {
644 const int len = strlen(str);
645 modified = false;
646 if (len > 0)
647 {
648 char *cp = str + (len - 1);
649 if (strchr(what_to_delete, *cp) != NULL)
650 {
651 *cp = '\0';
652 modified = true;
653 }
654 }
655 } while (modified);
656 }
657
658 /*
659 * Allocate a string
660 */
661 char *
662 #ifdef DMALLOC
string_alloc_debug(const char * str,struct gc_arena * gc,const char * file,int line)663 string_alloc_debug(const char *str, struct gc_arena *gc, const char *file, int line)
664 #else
665 string_alloc(const char *str, struct gc_arena *gc)
666 #endif
667 {
668 if (str)
669 {
670 const int n = strlen(str) + 1;
671 char *ret;
672
673 if (gc)
674 {
675 #ifdef DMALLOC
676 ret = (char *) gc_malloc_debug(n, false, gc, file, line);
677 #else
678 ret = (char *) gc_malloc(n, false, gc);
679 #endif
680 }
681 else
682 {
683 /* If there are no garbage collector available, it's expected
684 * that the caller cleans up afterwards. This is coherent with the
685 * earlier behaviour when gc_malloc() would be called with gc == NULL
686 */
687 #ifdef DMALLOC
688 ret = openvpn_dmalloc(file, line, n);
689 #else
690 ret = calloc(1, n);
691 #endif
692 check_malloc_return(ret);
693 }
694 memcpy(ret, str, n);
695 return ret;
696 }
697 else
698 {
699 return NULL;
700 }
701 }
702
703 /*
704 * Erase all characters in a string
705 */
706 void
string_clear(char * str)707 string_clear(char *str)
708 {
709 if (str)
710 {
711 secure_memzero(str, strlen(str));
712 }
713 }
714
715 /*
716 * Return the length of a string array
717 */
718 int
string_array_len(const char ** array)719 string_array_len(const char **array)
720 {
721 int i = 0;
722 if (array)
723 {
724 while (array[i])
725 {
726 ++i;
727 }
728 }
729 return i;
730 }
731
732 char *
print_argv(const char ** p,struct gc_arena * gc,const unsigned int flags)733 print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
734 {
735 struct buffer out = alloc_buf_gc(256, gc);
736 int i = 0;
737 for (;; )
738 {
739 const char *cp = *p++;
740 if (!cp)
741 {
742 break;
743 }
744 if (i)
745 {
746 buf_printf(&out, " ");
747 }
748 if (flags & PA_BRACKET)
749 {
750 buf_printf(&out, "[%s]", cp);
751 }
752 else
753 {
754 buf_printf(&out, "%s", cp);
755 }
756 ++i;
757 }
758 return BSTR(&out);
759 }
760
761 /*
762 * Allocate a string inside a buffer
763 */
764 struct buffer
765 #ifdef DMALLOC
string_alloc_buf_debug(const char * str,struct gc_arena * gc,const char * file,int line)766 string_alloc_buf_debug(const char *str, struct gc_arena *gc, const char *file, int line)
767 #else
768 string_alloc_buf(const char *str, struct gc_arena *gc)
769 #endif
770 {
771 struct buffer buf;
772
773 ASSERT(str);
774
775 #ifdef DMALLOC
776 buf_set_read(&buf, (uint8_t *) string_alloc_debug(str, gc, file, line), strlen(str) + 1);
777 #else
778 buf_set_read(&buf, (uint8_t *) string_alloc(str, gc), strlen(str) + 1);
779 #endif
780
781 if (buf.len > 0) /* Don't count trailing '\0' as part of length */
782 {
783 --buf.len;
784 }
785
786 return buf;
787 }
788
789 /*
790 * String comparison
791 */
792
793 bool
buf_string_match_head_str(const struct buffer * src,const char * match)794 buf_string_match_head_str(const struct buffer *src, const char *match)
795 {
796 const int size = strlen(match);
797 if (size < 0 || size > src->len)
798 {
799 return false;
800 }
801 return memcmp(BPTR(src), match, size) == 0;
802 }
803
804 bool
buf_string_compare_advance(struct buffer * src,const char * match)805 buf_string_compare_advance(struct buffer *src, const char *match)
806 {
807 if (buf_string_match_head_str(src, match))
808 {
809 buf_advance(src, strlen(match));
810 return true;
811 }
812 else
813 {
814 return false;
815 }
816 }
817
818 int
buf_substring_len(const struct buffer * buf,int delim)819 buf_substring_len(const struct buffer *buf, int delim)
820 {
821 int i = 0;
822 struct buffer tmp = *buf;
823 int c;
824
825 while ((c = buf_read_u8(&tmp)) >= 0)
826 {
827 ++i;
828 if (c == delim)
829 {
830 return i;
831 }
832 }
833 return -1;
834 }
835
836 /*
837 * String parsing
838 */
839
840 bool
buf_parse(struct buffer * buf,const int delim,char * line,const int size)841 buf_parse(struct buffer *buf, const int delim, char *line, const int size)
842 {
843 bool eol = false;
844 int n = 0;
845 int c;
846
847 ASSERT(size > 0);
848
849 do
850 {
851 c = buf_read_u8(buf);
852 if (c < 0)
853 {
854 eol = true;
855 }
856 if (c <= 0 || c == delim)
857 {
858 c = 0;
859 }
860 if (n >= size)
861 {
862 break;
863 }
864 line[n++] = c;
865 }
866 while (c);
867
868 line[size-1] = '\0';
869 return !(eol && !strlen(line));
870 }
871
872 /*
873 * Print a string which might be NULL
874 */
875 const char *
np(const char * str)876 np(const char *str)
877 {
878 if (str)
879 {
880 return str;
881 }
882 else
883 {
884 return "[NULL]";
885 }
886 }
887
888 /*
889 * Classify and mutate strings based on character types.
890 */
891
892 bool
char_class(const unsigned char c,const unsigned int flags)893 char_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 if ((flags & CC_PRINT) && (c >= 32 && c != 127)) /* allow ascii non-control and UTF-8, consider DEL to be a control */
930 {
931 return true;
932 }
933 if ((flags & CC_PUNCT) && ispunct(c))
934 {
935 return true;
936 }
937 if ((flags & CC_SPACE) && isspace(c))
938 {
939 return true;
940 }
941 if ((flags & CC_XDIGIT) && isxdigit(c))
942 {
943 return true;
944 }
945
946 if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
947 {
948 return true;
949 }
950 if ((flags & CC_NEWLINE) && c == '\n')
951 {
952 return true;
953 }
954 if ((flags & CC_CR) && c == '\r')
955 {
956 return true;
957 }
958
959 if ((flags & CC_BACKSLASH) && c == '\\')
960 {
961 return true;
962 }
963 if ((flags & CC_UNDERBAR) && c == '_')
964 {
965 return true;
966 }
967 if ((flags & CC_DASH) && c == '-')
968 {
969 return true;
970 }
971 if ((flags & CC_DOT) && c == '.')
972 {
973 return true;
974 }
975 if ((flags & CC_COMMA) && c == ',')
976 {
977 return true;
978 }
979 if ((flags & CC_COLON) && c == ':')
980 {
981 return true;
982 }
983 if ((flags & CC_SLASH) && c == '/')
984 {
985 return true;
986 }
987 if ((flags & CC_SINGLE_QUOTE) && c == '\'')
988 {
989 return true;
990 }
991 if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
992 {
993 return true;
994 }
995 if ((flags & CC_REVERSE_QUOTE) && c == '`')
996 {
997 return true;
998 }
999 if ((flags & CC_AT) && c == '@')
1000 {
1001 return true;
1002 }
1003 if ((flags & CC_EQUAL) && c == '=')
1004 {
1005 return true;
1006 }
1007 if ((flags & CC_LESS_THAN) && c == '<')
1008 {
1009 return true;
1010 }
1011 if ((flags & CC_GREATER_THAN) && c == '>')
1012 {
1013 return true;
1014 }
1015 if ((flags & CC_PIPE) && c == '|')
1016 {
1017 return true;
1018 }
1019 if ((flags & CC_QUESTION_MARK) && c == '?')
1020 {
1021 return true;
1022 }
1023 if ((flags & CC_ASTERISK) && c == '*')
1024 {
1025 return true;
1026 }
1027
1028 return false;
1029 }
1030
1031 static inline bool
char_inc_exc(const char c,const unsigned int inclusive,const unsigned int exclusive)1032 char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
1033 {
1034 return char_class(c, inclusive) && !char_class(c, exclusive);
1035 }
1036
1037 bool
string_class(const char * str,const unsigned int inclusive,const unsigned int exclusive)1038 string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
1039 {
1040 char c;
1041 ASSERT(str);
1042 while ((c = *str++))
1043 {
1044 if (!char_inc_exc(c, inclusive, exclusive))
1045 {
1046 return false;
1047 }
1048 }
1049 return true;
1050 }
1051
1052 /*
1053 * Modify string in place.
1054 * Guaranteed to not increase string length.
1055 */
1056 bool
string_mod(char * str,const unsigned int inclusive,const unsigned int exclusive,const char replace)1057 string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
1058 {
1059 const char *in = str;
1060 bool ret = true;
1061
1062 ASSERT(str);
1063
1064 while (true)
1065 {
1066 char c = *in++;
1067 if (c)
1068 {
1069 if (!char_inc_exc(c, inclusive, exclusive))
1070 {
1071 c = replace;
1072 ret = false;
1073 }
1074 if (c)
1075 {
1076 *str++ = c;
1077 }
1078 }
1079 else
1080 {
1081 *str = '\0';
1082 break;
1083 }
1084 }
1085 return ret;
1086 }
1087
1088 const char *
string_mod_const(const char * str,const unsigned int inclusive,const unsigned int exclusive,const char replace,struct gc_arena * gc)1089 string_mod_const(const char *str,
1090 const unsigned int inclusive,
1091 const unsigned int exclusive,
1092 const char replace,
1093 struct gc_arena *gc)
1094 {
1095 if (str)
1096 {
1097 char *buf = string_alloc(str, gc);
1098 string_mod(buf, inclusive, exclusive, replace);
1099 return buf;
1100 }
1101 else
1102 {
1103 return NULL;
1104 }
1105 }
1106
1107 void
string_replace_leading(char * str,const char match,const char replace)1108 string_replace_leading(char *str, const char match, const char replace)
1109 {
1110 ASSERT(match != '\0');
1111 while (*str)
1112 {
1113 if (*str == match)
1114 {
1115 *str = replace;
1116 }
1117 else
1118 {
1119 break;
1120 }
1121 ++str;
1122 }
1123 }
1124
1125 #ifdef CHARACTER_CLASS_DEBUG
1126
1127 #define CC_INCLUDE (CC_PRINT)
1128 #define CC_EXCLUDE (0)
1129 #define CC_REPLACE ('.')
1130
1131 void
character_class_debug(void)1132 character_class_debug(void)
1133 {
1134 char buf[256];
1135
1136 while (fgets(buf, sizeof(buf), stdin) != NULL)
1137 {
1138 string_mod(buf, CC_INCLUDE, CC_EXCLUDE, CC_REPLACE);
1139 printf("%s", buf);
1140 }
1141 }
1142
1143 #endif
1144
1145 #ifdef VERIFY_ALIGNMENT
1146 void
valign4(const struct buffer * buf,const char * file,const int line)1147 valign4(const struct buffer *buf, const char *file, const int line)
1148 {
1149 if (buf && buf->len)
1150 {
1151 int msglevel = D_ALIGN_DEBUG;
1152 const unsigned int u = (unsigned int) BPTR(buf);
1153
1154 if (u & (PAYLOAD_ALIGN-1))
1155 {
1156 msglevel = D_ALIGN_ERRORS;
1157 }
1158
1159 msg(msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
1160 (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "",
1161 file,
1162 line,
1163 (ptr_type)buf->data,
1164 buf->offset,
1165 buf->len,
1166 buf->capacity,
1167 buf_debug_file(buf),
1168 buf_debug_line(buf));
1169 }
1170 }
1171 #endif /* ifdef VERIFY_ALIGNMENT */
1172
1173 /*
1174 * struct buffer_list
1175 */
1176 struct buffer_list *
buffer_list_new(const int max_size)1177 buffer_list_new(const int max_size)
1178 {
1179 struct buffer_list *ret;
1180 ALLOC_OBJ_CLEAR(ret, struct buffer_list);
1181 ret->max_size = max_size;
1182 ret->size = 0;
1183 return ret;
1184 }
1185
1186 void
buffer_list_free(struct buffer_list * ol)1187 buffer_list_free(struct buffer_list *ol)
1188 {
1189 if (ol)
1190 {
1191 buffer_list_reset(ol);
1192 free(ol);
1193 }
1194 }
1195
1196 bool
buffer_list_defined(const struct buffer_list * ol)1197 buffer_list_defined(const struct buffer_list *ol)
1198 {
1199 return ol && ol->head != NULL;
1200 }
1201
1202 void
buffer_list_reset(struct buffer_list * ol)1203 buffer_list_reset(struct buffer_list *ol)
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
1217 void
buffer_list_push(struct buffer_list * ol,const char * str)1218 buffer_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 = len; /* Don't count trailing '\0' as part of length */
1227 }
1228 }
1229 }
1230
1231 struct buffer_entry *
buffer_list_push_data(struct buffer_list * ol,const void * data,size_t size)1232 buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
1233 {
1234 struct buffer_entry *e = NULL;
1235 if (data && (!ol->max_size || ol->size < ol->max_size))
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
1258 struct buffer *
buffer_list_peek(struct buffer_list * ol)1259 buffer_list_peek(struct buffer_list *ol)
1260 {
1261 if (ol && ol->head)
1262 {
1263 return &ol->head->buf;
1264 }
1265 else
1266 {
1267 return NULL;
1268 }
1269 }
1270
1271 void
buffer_list_aggregate_separator(struct buffer_list * bl,const size_t max_len,const char * sep)1272 buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len,
1273 const char *sep)
1274 {
1275 const int sep_len = strlen(sep);
1276 struct buffer_entry *more = bl->head;
1277 size_t size = 0;
1278 int count = 0;
1279 for (count = 0; more; ++count)
1280 {
1281 size_t extra_len = BLEN(&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
1317 void
buffer_list_aggregate(struct buffer_list * bl,const size_t max)1318 buffer_list_aggregate(struct buffer_list *bl, const size_t max)
1319 {
1320 buffer_list_aggregate_separator(bl, max, "");
1321 }
1322
1323 void
buffer_list_pop(struct buffer_list * ol)1324 buffer_list_pop(struct buffer_list *ol)
1325 {
1326 if (ol && ol->head)
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
1340 void
buffer_list_advance(struct buffer_list * ol,int n)1341 buffer_list_advance(struct buffer_list *ol, int 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 {
1349 buffer_list_pop(ol);
1350 }
1351 }
1352 }
1353
1354 struct buffer_list *
buffer_list_file(const char * fn,int max_line_len)1355 buffer_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(0);
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
1377 struct buffer
buffer_read_from_file(const char * filename,struct gc_arena * gc)1378 buffer_read_from_file(const char *filename, struct gc_arena *gc)
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 ssize_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 }
1402 ASSERT(buf_inc_len(&ret, read_size));
1403 buf_null_terminate(&ret);
1404
1405 cleanup:
1406 fclose(fp);
1407 return ret;
1408 }
1409