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