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