Coverage Report

Created: 2026-08-08 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/sbuff.c
Line
Count
Source
1
/*
2
 *   This library is free software; you can redistribute it and/or
3
 *   modify it under the terms of the GNU Lesser General Public
4
 *   License as published by the Free Software Foundation; either
5
 *   version 2.1 of the License, or (at your option) any later version.
6
 *
7
 *   This library is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
 *   Lesser General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU Lesser General Public
13
 *   License along with this library; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/** A generic string buffer structure for string printing and parsing
18
 *
19
 * @file src/lib/util/sbuff.c
20
 *
21
 * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22
 */
23
RCSID("$Id: 6e6cb8fe09bf95a937c78f8ff09b42ab65de2653 $")
24
25
#include <freeradius-devel/util/misc.h>
26
#include <freeradius-devel/util/syserror.h>
27
#include <freeradius-devel/util/atexit.h>
28
29
30
static _Thread_local char *sbuff_scratch;
31
32
/** When true, prevent use of the scratch space
33
 *
34
 * This prevents us from initialising a pool after the thread local destructors have run.
35
 *
36
 * The destructors may be called manually before thread exit, and we don't want to re-initialise the pool
37
 */
38
static _Thread_local bool sbuff_scratch_freed;
39
40
static_assert(sizeof(long long) >= sizeof(int64_t), "long long must be as wide or wider than an int64_t");
41
static_assert(sizeof(unsigned long long) >= sizeof(uint64_t), "long long must be as wide or wider than an uint64_t");
42
43
fr_table_num_ordered_t const sbuff_parse_error_table[] = {
44
  { L("ok"),      FR_SBUFF_PARSE_OK       },
45
  { L("token not found"),   FR_SBUFF_PARSE_ERROR_NOT_FOUND      },
46
  { L("trailing data"),   FR_SBUFF_PARSE_ERROR_TRAILING     },
47
  { L("token format invalid"),  FR_SBUFF_PARSE_ERROR_FORMAT     },
48
  { L("out of space"),    FR_SBUFF_PARSE_ERROR_OUT_OF_SPACE   },
49
  { L("integer overflow"),  FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW   },
50
  { L("integer underflow"), FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW    },
51
  { L("empty input is invalid"),  FR_SBUFF_PARSE_ERROR_INPUT_EMPTY    },
52
};
53
size_t sbuff_parse_error_table_len = NUM_ELEMENTS(sbuff_parse_error_table);
54
55
#if defined(STATIC_ANALYZER) || !defined(NDEBUG)
56
85.3M
#  define CHECK_SBUFF_INIT(_sbuff)  do { if (!(_sbuff)->extend && (unlikely(!(_sbuff)->buff) || unlikely(!(_sbuff)->start) || unlikely(!(_sbuff)->end) || unlikely(!(_sbuff)->p))) return 0; } while (0)
57
84.4M
#  define CHECK_SBUFF_WRITEABLE(_sbuff) do { CHECK_SBUFF_INIT(_sbuff); if (unlikely((_sbuff)->is_const)) return 0; } while (0)
58
59
#else
60
#  define CHECK_SBUFF_INIT(_sbuff)
61
#  define CHECK_SBUFF_WRITEABLE(_sbuff)
62
#endif
63
64
bool const sbuff_char_class_uint[SBUFF_CHAR_CLASS] = {
65
  SBUFF_CHAR_CLASS_NUM,
66
  ['+'] = true
67
};
68
69
bool const sbuff_char_class_int[SBUFF_CHAR_CLASS] = {
70
  SBUFF_CHAR_CLASS_NUM,
71
  ['+'] = true, ['-'] = true
72
};
73
74
bool const sbuff_char_class_float[SBUFF_CHAR_CLASS] = {
75
  SBUFF_CHAR_CLASS_NUM,
76
  ['-'] = true, ['+'] = true, ['e'] = true, ['E'] = true, ['.'] = true,
77
};
78
79
bool const sbuff_char_class_zero[SBUFF_CHAR_CLASS] = {
80
  ['0'] = true
81
};
82
83
/*
84
 *  Anything which vaguely resembles an IP address, prefix, or host name.
85
 */
86
bool const sbuff_char_class_hostname[SBUFF_CHAR_CLASS] = {
87
  SBUFF_CHAR_CLASS_ALPHA_NUM,
88
  ['.'] = true,   /* only for IPv4 and host names */
89
  [':'] = true,   /* only for IPv6 numerical addresses */
90
  ['-'] = true,   /* only for host names */
91
  ['/'] = true,   /* only for prefixes */
92
  ['['] = true,   /* only for IPv6 numerical addresses */
93
  [']'] = true,   /* only for IPv6 numerical addresses */
94
  ['_'] = true,   /* only for certain host name labels */
95
  ['*'] = true,   /* really only for ipv4 addresses */
96
};
97
98
bool const sbuff_char_class_hex[SBUFF_CHAR_CLASS] = { SBUFF_CHAR_CLASS_HEX };
99
bool const sbuff_char_alpha_num[SBUFF_CHAR_CLASS] = { SBUFF_CHAR_CLASS_ALPHA_NUM };
100
bool const sbuff_char_word[SBUFF_CHAR_CLASS] = {
101
  SBUFF_CHAR_CLASS_ALPHA_NUM,
102
  ['-'] = true, ['_'] = true,
103
};
104
bool const sbuff_char_whitespace[SBUFF_CHAR_CLASS] = {
105
  ['\t'] = true, ['\n'] = true, ['\r'] = true, ['\f'] = true, ['\v'] = true, [' '] = true,
106
};
107
108
bool const sbuff_char_line_endings[SBUFF_CHAR_CLASS] = {
109
  ['\n'] = true, ['\r'] = true
110
};
111
112
bool const sbuff_char_blank[SBUFF_CHAR_CLASS] = {
113
  ['\t'] = true, [' '] = true,
114
};
115
116
/** Copy function that allows overlapping memory ranges to be copied
117
 *
118
 * @param[out] o_start    start of output buffer.
119
 * @param[in] o_end   end of the output buffer.
120
 * @param[in] i_start   start of the input buffer.
121
 * @param[in] i_end   end of data to copy.
122
 * @return
123
 *  - >0 the number of bytes copied.
124
 *      - 0 invalid args.
125
 *      - <0 the number of bytes we'd need to complete the copy.
126
 */
127
static inline CC_HINT(always_inline) ssize_t safecpy(char *o_start, char *o_end,
128
                 char const *i_start, char const *i_end)
129
42.0M
{
130
42.0M
  ssize_t diff;
131
42.0M
  size_t  i_len = i_end - i_start;
132
133
42.0M
  if (unlikely((o_end < o_start) || (i_end < i_start))) return 0; /* sanity check */
134
135
42.0M
  diff = (o_end - o_start) - (i_len);
136
42.0M
  if (diff < 0) return diff;
137
138
42.0M
  if ((i_start > o_end) || (i_end < o_start)) {     /* no-overlap */
139
42.0M
    memcpy(o_start,  i_start, i_len);
140
42.0M
  } else {             /* overlap */
141
0
    memmove(o_start, i_start, i_len);
142
0
  }
143
144
42.0M
  return (i_len);
145
42.0M
}
146
147
static inline CC_HINT(always_inline) size_t min(size_t x, size_t y)
148
373k
{
149
373k
  return x < y ? x : y;
150
373k
}
151
152
/** Update all markers and pointers in the set of sbuffs to point to new_buff
153
 *
154
 * This function should be used if the underlying buffer is realloced.
155
 *
156
 * @param[in] sbuff to update.
157
 * @param[in] new_buff  to assign to to sbuff.
158
 * @param[in] new_len Length of the new buffer.
159
 */
160
void fr_sbuff_update(fr_sbuff_t *sbuff, char *new_buff, size_t new_len)
161
102k
{
162
102k
  fr_sbuff_t    *sbuff_i;
163
102k
  char      *old_buff;  /* Current buff */
164
165
102k
  old_buff = sbuff->buff;
166
167
  /*
168
   *  Update pointers to point to positions
169
   *  in new buffer based on their relative
170
   *  offsets in the old buffer... but not
171
   *  past the end of the new buffer.
172
   */
173
288k
  for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) {
174
186k
    fr_sbuff_marker_t *m_i;
175
176
186k
    sbuff_i->buff = new_buff;
177
186k
    sbuff_i->start = new_buff + min(new_len, sbuff_i->start - old_buff);
178
186k
    sbuff_i->end = sbuff_i->buff + new_len;
179
186k
    *(sbuff_i->end) = '\0'; /* Re-terminate */
180
181
186k
    sbuff_i->p = new_buff + min(new_len, sbuff_i->p - old_buff);
182
183
186k
    for (m_i = sbuff_i->m; m_i; m_i = m_i->next) m_i->p = new_buff + min(new_len, m_i->p - old_buff);
184
186k
  }
185
102k
}
186
187
/** Shift the contents of the sbuff, returning the number of bytes we managed to shift
188
 *
189
 * @param[in] sbuff to shift.
190
 * @param[in] shift the contents of the buffer this many bytes
191
 *      towards the start of the buffer.
192
 * @param[in] move_end  If the buffer is used for reading, then this should be true
193
 *      so we cannot read passed the end of valid data.
194
 * @return
195
 *  - 0 the shift failed due to constraining pointers.
196
 *  - >0 the number of bytes we managed to shift pointers
197
 *    in the sbuff.  memmove should be used to move the
198
 *    existing contents of the buffer, and fill the free
199
 *    space at the end of the buffer with additional data.
200
 */
201
size_t fr_sbuff_shift(fr_sbuff_t *sbuff, size_t shift, bool move_end)
202
0
{
203
0
  fr_sbuff_t    *sbuff_i;
204
0
  char      *buff, *end;    /* Current start */
205
0
  size_t      max_shift = shift;
206
0
  bool      reterminate = false;
207
208
0
  CHECK_SBUFF_INIT(sbuff);
209
210
0
  buff = sbuff->buff;
211
0
  end = sbuff->end;
212
213
  /*
214
   *  If the sbuff is already \0 terminated
215
   *  and we're not working on a const buffer
216
   *  then assume we need to re-terminate
217
   *  later.
218
   */
219
0
  reterminate = (sbuff->p < sbuff->end) && (*sbuff->p == '\0') && !sbuff->is_const;
220
221
  /*
222
   *  First pass: find the maximum shift, which is the minimum
223
   *  of the distances from buff to any of the current pointers
224
   *  or current pointers of markers of dbuff and its ancestors.
225
   *  (We're also constrained by the requested shift count.)
226
   */
227
0
  for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) {
228
0
    fr_sbuff_marker_t *m_i;
229
230
0
    max_shift = min(max_shift, sbuff_i->p - buff);
231
0
    if (!max_shift) return 0;
232
233
0
    for (m_i = sbuff_i->m; m_i; m_i = m_i->next) {
234
0
      max_shift = min(max_shift, m_i->p - buff);
235
0
      if (!max_shift) return 0;
236
0
    }
237
0
  }
238
239
  /*
240
   *  Second pass: adjust pointers.
241
   *  The first pass means we need only subtract shift from
242
   *  current pointers.  Start pointers can't constrain shift,
243
   *  or we'd never free any space, so they require the added
244
   *  check.
245
   */
246
0
  for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) {
247
0
    fr_sbuff_marker_t *m_i;
248
0
    char      *start = sbuff_i->start;
249
250
0
    sbuff_i->start -= min(max_shift, sbuff_i->start - buff);
251
0
    sbuff_i->p -= max_shift;
252
0
    if (move_end) sbuff_i->end -= max_shift;
253
0
    sbuff_i->shifted += (max_shift - (start - sbuff_i->start));
254
0
    for (m_i = sbuff_i->m; m_i; m_i = m_i->next) m_i->p -= max_shift;
255
0
  }
256
257
  /*
258
   *  Only memmove if the shift wasn't the
259
   *      entire contents of the buffer.
260
   */
261
0
  if ((buff + max_shift) < end) memmove(buff, buff + max_shift, end - (buff + max_shift));
262
263
0
  if (reterminate) *sbuff->p = '\0';
264
265
0
  return max_shift;
266
0
}
267
268
/** Refresh the buffer with more data from the file
269
 *
270
 */
271
size_t fr_sbuff_extend_file(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension)
272
0
{
273
0
  fr_sbuff_t    *sbuff_i;
274
0
  size_t      read, available, total_read, shift;
275
0
  fr_sbuff_uctx_file_t  *fctx;
276
277
0
  CHECK_SBUFF_INIT(sbuff);
278
279
0
  fctx = sbuff->uctx;
280
0
  if (fctx->eof) return 0;
281
282
0
  if (extension == SIZE_MAX) extension = 0;
283
284
0
  total_read = fctx->shifted + (sbuff->end - sbuff->buff);
285
0
  if (total_read >= fctx->max) {
286
0
    fr_strerror_const("Can't satisfy extension request, max bytes read");
287
0
    return 0; /* There's no way we could satisfy the extension request */
288
0
  }
289
290
  /*
291
   *  Shift out the maximum number of bytes we can
292
   *  irrespective of the amount that was requested
293
   *  as the extension.  It's more efficient to do
294
   *  this than lots of small shifts, and just
295
   *  looking and the number of bytes used in the
296
   *  deepest sbuff, and using that as the shift
297
   *  amount, might mean we don't shift anything at
298
   *  all!
299
   *
300
   *  fr_sbuff_shift will cap the max shift amount,
301
   *  so markers and positions will remain valid for
302
   *  all sbuffs in the chain.
303
   */
304
0
  shift = fr_sbuff_current(sbuff) - fr_sbuff_buff(sbuff);
305
0
  if (shift) {
306
    /*
307
     *  Try and shift as much as we can out
308
     *  of the buffer to make space.
309
     *
310
     *  Note: p and markers are constraints here.
311
     */
312
0
    fctx->shifted += fr_sbuff_shift(sbuff, shift, true);
313
0
  }
314
315
0
  available = fctx->buff_end - sbuff->end;
316
0
  if (available > (fctx->max - total_read)) available = fctx->max - total_read;
317
0
  if (available < extension) {
318
0
    fr_strerror_printf("Can't satisfy extension request for %zu bytes", extension);
319
0
    return 0; /* There's no way we could satisfy the extension request */
320
0
  }
321
322
0
  read = fread(sbuff->end, 1, available, fctx->file);
323
0
  for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) {
324
0
    sbuff_i->end += read; /* Advance end, which increases fr_sbuff_remaining() */
325
0
  }
326
327
  /** Check for errors
328
   */
329
0
  if (read < available) {
330
0
    if (!feof(fctx->file)) {
331
      /*
332
       *  It's an error, but ferror() returns a ??? error number,
333
       *  and not errno.
334
       *
335
       *  Posix says "The ferror() function shall not change the setting of errno if
336
       *  stream is valid".  And the return value is defined to be non-zero, but with no
337
       *  meaning associated with any non-zero values.
338
       */
339
0
      fr_strerror_printf("Error extending buffer: %d", ferror(fctx->file));
340
0
      *status |= FR_SBUFF_FLAG_EXTEND_ERROR;
341
0
      return 0;
342
0
    }
343
344
0
    fctx->eof = true;
345
0
  }
346
347
0
  return read;
348
0
}
349
350
/** Accessor function for the EOF state of the file extendor
351
 *
352
 */
353
bool fr_sbuff_eof_file(fr_sbuff_t *sbuff)
354
0
{
355
0
  fr_sbuff_uctx_file_t  *fctx = sbuff->uctx;
356
0
  return fctx->eof;
357
0
}
358
359
/** Reallocate the current buffer
360
 *
361
 * @param[in] status    Extend status.
362
 * @param[in] sbuff   to be extended.
363
 * @param[in] extension   How many additional bytes should be allocated
364
 *        in the buffer.
365
 * @return
366
 *  - 0 the extension operation failed.
367
 *  - >0 the number of bytes the buffer was extended by.
368
 */
369
size_t fr_sbuff_extend_talloc(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension)
370
44.7k
{
371
44.7k
  fr_sbuff_uctx_talloc_t  *tctx = sbuff->uctx;
372
44.7k
  size_t      clen, nlen, elen = extension;
373
44.7k
  char      *new_buff;
374
375
44.7k
  CHECK_SBUFF_INIT(sbuff);
376
377
44.7k
  clen = sbuff->buff ? talloc_array_length(sbuff->buff) : 0;
378
  /*
379
   *  If the current buffer size + the extension
380
   *  is less than init, extend the buffer to init.
381
   *
382
   *  This can happen if the buffer has been
383
   *  trimmed, and then additional data is added.
384
   */
385
44.7k
  if ((clen + elen) < tctx->init) {
386
0
    elen = (tctx->init - clen) + 1; /* add \0 */
387
  /*
388
   *  Double the buffer size if it's more than the
389
   *  requested amount.
390
   */
391
44.7k
  } else if (elen < clen) {
392
31.7k
    elen = clen - 1;    /* Don't double alloc \0 */
393
31.7k
  }
394
395
  /*
396
   *  Check we don't exceed the maximum buffer
397
   *  length, including the NUL byte.
398
   */
399
44.7k
  if (tctx->max && ((clen + elen + 1) > tctx->max)) {
400
33
    elen = tctx->max - clen;
401
33
    if (elen == 0) {
402
0
      fr_strerror_printf("Failed extending buffer by %zu bytes to "
403
0
             "%zu bytes, max is %zu bytes",
404
0
             extension, clen + extension, tctx->max);
405
0
      return 0;
406
0
    }
407
33
    elen += 1;      /* add \0 */
408
33
  }
409
44.7k
  nlen = clen + elen;
410
411
44.7k
  new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, nlen);
412
44.7k
  if (unlikely(!new_buff)) {
413
0
    fr_strerror_printf("Failed extending buffer by %zu bytes to %zu bytes", elen, nlen);
414
0
    *status |= FR_SBUFF_FLAG_EXTEND_ERROR;
415
0
    return 0;
416
0
  }
417
418
44.7k
  (void)fr_sbuff_update(sbuff, new_buff, nlen - 1); /* Shouldn't fail as we're extending */
419
420
44.7k
  return elen;
421
44.7k
}
422
423
/** Trim a talloced sbuff to the minimum length required to represent the contained string
424
 *
425
 * @param[in] sbuff to trim.
426
 * @param[in] len Length to trim to.  Passing SIZE_MAX will
427
 *      result in the buffer being trimmed to the
428
 *      length of the content.
429
 * @return
430
 *  - 0 on success.
431
 *  - -1 on failure - markers present pointing past the end of string data.
432
 */
433
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
434
47.8k
{
435
47.8k
  size_t      clen = 0, nlen = 1;
436
47.8k
  char      *new_buff;
437
47.8k
  fr_sbuff_uctx_talloc_t  *tctx = sbuff->uctx;
438
439
47.8k
  CHECK_SBUFF_INIT(sbuff);
440
441
47.8k
  if (sbuff->buff) clen = talloc_array_length(sbuff->buff);
442
443
47.8k
  if (len != SIZE_MAX) {
444
0
    nlen += len;
445
47.8k
  } else if (sbuff->buff){
446
47.8k
    nlen += (sbuff->p - sbuff->start);
447
47.8k
  }
448
449
47.8k
  if (nlen != clen) {
450
42.0k
    new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, nlen);
451
42.0k
    if (unlikely(!new_buff)) {
452
0
      fr_strerror_printf("Failed trimming buffer from %zu to %zu", clen, nlen);
453
0
      return -1;
454
0
    }
455
42.0k
    fr_sbuff_update(sbuff, new_buff, nlen - 1);
456
42.0k
  }
457
458
47.8k
  return 0;
459
47.8k
}
460
461
/** Reset a talloced buffer to its initial length, clearing any data stored
462
 *
463
 * @param[in] sbuff to reset.
464
 * @return
465
 *  - 0 on success.
466
 *  - -1 on failure - markers present pointing past the end of string data.
467
 */
468
int fr_sbuff_reset_talloc(fr_sbuff_t *sbuff)
469
15.5k
{
470
15.5k
  fr_sbuff_uctx_talloc_t  *tctx = sbuff->uctx;
471
472
15.5k
  CHECK_SBUFF_INIT(sbuff);
473
474
15.5k
  fr_sbuff_set_to_start(sbuff); /* Clear data */
475
15.5k
  sbuff->m = NULL;   /* Remove any maker references */
476
477
15.5k
  if (fr_sbuff_used(sbuff) != tctx->init) {
478
15.5k
    char *new_buff;
479
480
15.5k
    new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, tctx->init);
481
15.5k
    if (!new_buff) {
482
0
      fr_strerror_printf("Failed reallocing from %zu to %zu",
483
0
             talloc_array_length(sbuff->buff), tctx->init);
484
0
      return -1;
485
0
    }
486
15.5k
    sbuff->buff = new_buff;
487
15.5k
    fr_sbuff_update(sbuff, new_buff, tctx->init - 1);
488
15.5k
  }
489
490
15.5k
  return 0;
491
15.5k
}
492
493
/** Fill as much of the output buffer we can and break on partial copy
494
 *
495
 * @param[in] _out  sbuff to write to.
496
 * @param[in] _in sbuff to copy from.
497
 * @param[in] _len  maximum amount to copy.
498
 */
499
210k
#define FILL_OR_GOTO_DONE(_out, _in, _len) if (fr_sbuff_move(_out, _in, _len) < (size_t)(_len)) goto done
500
501
/** Constrain end pointer to prevent advancing more than the amount the caller specified
502
 *
503
 * @param[in] _sbuff  to constrain.
504
 * @param[in] _max  maximum amount to advance.
505
 * @param[in] _used how much we've advanced so far.
506
 * @return a temporary end pointer.
507
 */
508
#define CONSTRAINED_END(_sbuff, _max, _used) \
509
234k
  (((_max) - (_used)) > fr_sbuff_remaining(_sbuff) ? (_sbuff)->end : (_sbuff)->p + ((_max) - (_used)))
510
511
512
/** Populate a terminal index
513
 *
514
 * @param[out] needle_len the longest needle.  Will not be set
515
 *        if the terminal array is empty.
516
 * @param[out] idx    to populate.
517
 * @param[in] term    Terminals to populate the index with.
518
 */
519
static inline CC_HINT(always_inline) void fr_sbuff_terminal_idx_init(size_t *needle_len,
520
                     uint8_t idx[static SBUFF_CHAR_CLASS],
521
                     fr_sbuff_term_t const *term)
522
77.6k
{
523
77.6k
  size_t i, len, max = 0;
524
525
77.6k
  if (!term) return;
526
527
76.9k
  memset(idx, 0, SBUFF_CHAR_CLASS);
528
529
1.09M
  for (i = 0; i < term->len; i++) {
530
1.02M
    len = term->elem[i].len;
531
1.02M
    if (len > max) max = len;
532
533
1.02M
    idx[(uint8_t)term->elem[i].str[0]] = i + 1;
534
1.02M
  }
535
536
76.9k
  if (i > 0) *needle_len = max;
537
76.9k
}
538
539
/** Efficient terminal string search
540
 *
541
 * Caller should ensure that a buffer extension of needle_len bytes has been requested
542
 * before calling this function.
543
 *
544
 * @param[in] in    Sbuff to search in.
545
 * @param[in] p     Current position (may be ahead of in->p).
546
 * @param[in] idx   Fastpath index, populated by
547
 *        fr_sbuff_terminal_idx_init.
548
 * @param[in] term    terminals to search in.
549
 * @param[in] needle_len  Length of the longest needle.
550
 * @return
551
 *      - true if found.
552
 *  - false if not.
553
 */
554
static inline bool fr_sbuff_terminal_search(fr_sbuff_t *in, char const *p,
555
              uint8_t idx[static SBUFF_CHAR_CLASS],
556
              fr_sbuff_term_t const *term, UNUSED size_t needle_len)
557
25.2M
{
558
25.2M
  uint8_t   term_idx;
559
560
25.2M
  ssize_t   start = 0;
561
25.2M
  ssize_t   end;
562
25.2M
  ssize_t   mid;
563
564
25.2M
  size_t    remaining;
565
566
25.2M
  if (!term) return false;      /* If there's no terminals, we don't need to search */
567
568
265k
  end = term->len - 1;
569
570
265k
  term_idx = idx[(uint8_t)*p];      /* Fast path */
571
265k
  if (!term_idx) return false;
572
573
63.5k
  if (p > in->end) return false; /* paranoia */
574
575
  /*
576
   *  "p" may be ahead of "in->p", as the caller can scan forward without advancing "in->p`".  So we
577
   *  need to measure bytes available from "p".  Othwrwise using fr_sbuff_remaining(in) would
578
   *  over-state the available bytes by (p - in->p) and read past in->end.
579
   */
580
63.5k
  remaining = (size_t)(in->end - p);
581
582
  /*
583
   *  Special case for EOFlike states
584
   */
585
63.5k
  if (remaining == 0) {
586
9
    if (!fr_sbuff_is_extendable(in) && (idx['\0'] != 0)) return true;
587
0
    return false;
588
9
  }
589
590
63.5k
  mid = term_idx - 1;       /* Inform the mid point from the index */
591
592
67.7k
  while (start <= end) {
593
66.3k
    fr_sbuff_term_elem_t const  *elem;
594
66.3k
    size_t        tlen;
595
66.3k
    int       ret;
596
597
66.3k
    elem = &term->elem[mid];
598
66.3k
    tlen = elem->len;
599
600
66.3k
    ret = memcmp(p, elem->str, tlen < (size_t)remaining ? tlen : (size_t)remaining);
601
66.3k
    if (ret == 0) {
602
      /*
603
       *  If we have more text than the table element, that's fine
604
       */
605
62.1k
      if (remaining >= tlen) return true;
606
607
      /*
608
       *  If input was shorter than the table element we need to
609
       *  keep searching.
610
       */
611
0
      ret = -1;
612
0
    }
613
614
4.19k
    if (ret < 0) {
615
1.38k
      end = mid - 1;
616
2.81k
    } else {
617
2.81k
      start = mid + 1;
618
2.81k
    }
619
620
4.19k
    mid = start + ((end - start) / 2);  /* Avoid overflow */
621
4.19k
  }
622
623
1.42k
  return false;
624
63.5k
}
625
626
/** Compare two terminal elements for ordering purposes
627
 *
628
 * @param[in] a       first terminal to compare.
629
 * @param[in] b   second terminal to compare.
630
 * @return CMP(a,b)
631
 */
632
static inline int8_t terminal_cmp(fr_sbuff_term_elem_t const *a, fr_sbuff_term_elem_t const *b)
633
1.02M
{
634
1.02M
  return MEMCMP_FIELDS(a, b, str, len);
635
1.02M
}
636
637
#if 0
638
static void fr_sbuff_terminal_debug_tmp(fr_sbuff_term_elem_t const *elem[], size_t len)
639
{
640
  size_t i;
641
642
  FR_FAULT_LOG("Terminal count %zu", len);
643
644
  for (i = 0; i < len; i++) FR_FAULT_LOG("\t\"%s\" (%zu)", elem[i] ? elem[i]->str : "NULL", elem[i] ? elem[i]->len : 0);
645
}
646
#endif
647
648
/** Merge two sets of terminal strings
649
 *
650
 * @param[in] ctx to allocate the new terminal array in.
651
 * @param[in] a   first set of terminals to merge.
652
 * @param[in] b   second set of terminals to merge.
653
 * @return A new set of de-duplicated and sorted terminals.
654
 */
655
fr_sbuff_term_t *fr_sbuff_terminals_amerge(TALLOC_CTX *ctx, fr_sbuff_term_t const *a, fr_sbuff_term_t const *b)
656
22.7k
{
657
22.7k
  size_t        i, j, num;
658
22.7k
  fr_sbuff_term_t     *out;
659
22.7k
  fr_sbuff_term_elem_t const  *tmp[SBUFF_CHAR_CLASS];
660
661
  /*
662
   *  Check all inputs are pre-sorted.  It doesn't break this
663
   *  function, but it's useful in case the terminal arrays
664
   *  are defined elsewhere without merging.
665
   */
666
22.7k
#if !defined(NDEBUG) && defined(WITH_VERIFY_PTR)
667
320k
  if (a->len) for (i = 0; i < a->len - 1; i++) fr_assert(terminal_cmp(&a->elem[i], &a->elem[i + 1]) < 0);
668
127k
  if (b->len) for (i = 0; i < b->len - 1; i++) fr_assert(terminal_cmp(&b->elem[i], &b->elem[i + 1]) < 0);
669
22.7k
#endif
670
671
  /*
672
   *  Since the inputs are sorted, we can just do an O(n+m)
673
   *  walk through the arrays, comparing entries across the
674
   *  two arrays.
675
   *
676
   *  If there are duplicates, we prefer "a", for no particular reason.
677
   */
678
22.7k
  num = i = j = 0;
679
229k
  while ((i < a->len) && (j < b->len)) {
680
207k
    int8_t cmp;
681
682
207k
    cmp = terminal_cmp(&a->elem[i], &b->elem[j]);
683
207k
    if (cmp == 0) {
684
14.0k
      j++;
685
14.0k
      tmp[num++] = &a->elem[i++];
686
687
193k
    } else if (cmp < 0) {
688
114k
      tmp[num++] = &a->elem[i++];
689
690
114k
    } else if (cmp > 0) {
691
78.3k
      tmp[num++] = &b->elem[j++];
692
78.3k
    }
693
694
207k
    fr_assert(num < SBUFF_CHAR_CLASS);
695
207k
  }
696
697
  /*
698
   *  Only one of these will be hit, and it's simpler than nested "if" statements.
699
   */
700
214k
  while (i < a->len) tmp[num++] = &a->elem[i++];
701
58.2k
  while (j < b->len) tmp[num++] = &b->elem[j++];
702
703
22.7k
  out = talloc_pooled_object(ctx, fr_sbuff_term_t, num, num * sizeof(fr_sbuff_term_elem_t));
704
22.7k
  if (unlikely(!out)) return NULL;
705
706
22.7k
  out->elem = talloc_array(out, fr_sbuff_term_elem_t, num);
707
22.7k
  if (unlikely(!out->elem)) {
708
0
    talloc_free(out);
709
0
    return NULL;
710
0
  }
711
22.7k
  out->len = num;
712
713
457k
  for (i = 0; i < num; i++) out->elem[i] = *tmp[i]; /* copy merged results back */
714
715
22.7k
#if !defined(NDEBUG) && defined(WITH_VERIFY_PTR)
716
434k
  for (i = 0; i < num - 1; i++) fr_assert(terminal_cmp(&out->elem[i], &out->elem[i + 1]) < 0);
717
22.7k
#endif
718
719
22.7k
  return out;
720
22.7k
}
721
722
/** Copy as many bytes as possible from a sbuff to a sbuff
723
 *
724
 * Copy size is limited by available data in sbuff and space in output sbuff.
725
 *
726
 * @param[out] out  Where to copy to.
727
 * @param[in] in  Where to copy from.  Will copy len bytes from current position in buffer.
728
 * @param[in] len How many bytes to copy.  If SIZE_MAX the entire buffer will be copied.
729
 * @return
730
 *  - 0 no bytes copied.
731
 *  - >0 the number of bytes copied.
732
 */
733
size_t fr_sbuff_out_bstrncpy(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
734
150k
{
735
150k
  fr_sbuff_t  our_in = FR_SBUFF_BIND_CURRENT(in);
736
150k
  size_t    remaining;
737
738
150k
  CHECK_SBUFF_INIT(in);
739
740
300k
  while (fr_sbuff_used_total(&our_in) < len) {
741
234k
    size_t chunk_len;
742
743
234k
    remaining = (len - fr_sbuff_used_total(&our_in));
744
745
234k
    if (!fr_sbuff_extend(&our_in)) break;
746
747
150k
    chunk_len = fr_sbuff_remaining(&our_in);
748
150k
    if (chunk_len > remaining) chunk_len = remaining;
749
750
150k
    FILL_OR_GOTO_DONE(out, &our_in, chunk_len);
751
150k
  }
752
753
150k
done:
754
150k
  *out->p = '\0';
755
150k
  return fr_sbuff_used_total(&our_in);
756
150k
}
757
758
/** Copy exactly len bytes from a sbuff to a sbuff or fail
759
 *
760
 * Copy size is limited by available data in sbuff, space in output sbuff, and length.
761
 *
762
 * @param[out] out  Where to copy to.
763
 * @param[in] in  Where to copy from.  Will copy len bytes from current position in buffer.
764
 * @param[in] len How many bytes to copy.  If SIZE_MAX the entire buffer will be copied.
765
 * @return
766
 *  - 0 no bytes copied, no token found of sufficient length in input buffer.
767
 *  - >0 the number of bytes copied.
768
 *  - <0 the number of additional output bytes we would have needed to
769
 *    complete the copy.
770
 */
771
ssize_t fr_sbuff_out_bstrncpy_exact(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
772
3
{
773
3
  fr_sbuff_t    our_in = FR_SBUFF(in);
774
3
  size_t      remaining;
775
3
  fr_sbuff_marker_t m;
776
777
3
  CHECK_SBUFF_INIT(in);
778
779
3
  fr_sbuff_marker(&m, out);
780
781
3
  do {
782
3
    size_t chunk_len;
783
3
    ssize_t copied;
784
785
3
    remaining = (len - fr_sbuff_used_total(&our_in));
786
3
    if (remaining && !fr_sbuff_extend(&our_in)) {
787
0
      fr_sbuff_marker_release(&m);
788
0
      return 0;
789
0
    }
790
791
3
    chunk_len = fr_sbuff_remaining(&our_in);
792
3
    if (chunk_len > remaining) chunk_len = remaining;
793
794
3
    copied = fr_sbuff_in_bstrncpy(out, our_in.p, chunk_len);
795
3
    if (copied < 0) {
796
0
      fr_sbuff_set(out, &m);    /* Reset out */
797
0
      *m.p = '\0';      /* Re-terminate */
798
799
      /* Amount remaining in input buffer minus the amount we could have copied */
800
0
      if (len == SIZE_MAX) {
801
0
        fr_sbuff_marker_release(&m);
802
0
        return -(fr_sbuff_remaining(in) - (chunk_len + copied));
803
0
      }
804
      /* Amount remaining to copy minus the amount we could have copied */
805
0
      fr_sbuff_marker_release(&m);
806
0
      return -(remaining - (chunk_len + copied));
807
0
    }
808
3
    fr_sbuff_advance(&our_in, copied);
809
3
  } while (fr_sbuff_used_total(&our_in) < len);
810
811
3
  fr_sbuff_marker_release(&m);
812
813
3
  FR_SBUFF_SET_RETURN(in, &our_in);  /* in was pinned, so this works */
814
3
}
815
816
/** Copy as many allowed characters as possible from a sbuff to a sbuff
817
 *
818
 * Copy size is limited by available data in sbuff and output buffer length.
819
 *
820
 * As soon as a disallowed character is found the copy is stopped.
821
 * The input sbuff will be left pointing at the first disallowed character.
822
 *
823
 * @param[out] out    Where to copy to.
824
 * @param[in] in    Where to copy from.  Will copy len bytes from current position in buffer.
825
 * @param[in] len   How many bytes to copy.  If SIZE_MAX the entire buffer will be copied.
826
 * @param[in] allowed   Characters to include the copy.
827
 * @return
828
 *  - 0 no bytes copied.
829
 *  - >0 the number of bytes copied.
830
 */
831
size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len,
832
             bool const allowed[static SBUFF_CHAR_CLASS])
833
23.4k
{
834
23.4k
  fr_sbuff_t  our_in = FR_SBUFF_BIND_CURRENT(in);
835
836
23.4k
  CHECK_SBUFF_INIT(in);
837
838
37.5k
  while (fr_sbuff_used_total(&our_in) < len) {
839
37.5k
    char  *p;
840
37.5k
    char  *end;
841
842
37.5k
    if (!fr_sbuff_extend(&our_in)) break;
843
844
23.4k
    p = fr_sbuff_current(&our_in);
845
23.4k
    end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in));
846
847
7.79M
    while ((p < end) && allowed[(uint8_t)*p]) p++;
848
849
23.4k
    FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p);
850
851
23.3k
    if (p != end) break;   /* stopped early, break */
852
23.3k
  }
853
854
23.4k
done:
855
23.4k
  *out->p = '\0';
856
23.4k
  return fr_sbuff_used_total(&our_in);
857
23.4k
}
858
859
/** Copy as many allowed characters as possible from a sbuff to a sbuff
860
 *
861
 * Copy size is limited by available data in sbuff and output buffer length.
862
 *
863
 * As soon as a disallowed character is found the copy is stopped.
864
 * The input sbuff will be left pointing at the first disallowed character.
865
 *
866
 * @param[out] out    Where to copy to.
867
 * @param[in] in    Where to copy from.  Will copy len bytes from current position in buffer.
868
 * @param[in] len   How many bytes to copy.  If SIZE_MAX the entire buffer will be copied.
869
 * @param[in] tt    Token terminals in the encompassing grammar.
870
 * @param[in] u_rules   If not NULL, ignore characters in the until set when
871
 *        prefixed with u_rules->chr. FIXME - Should actually evaluate
872
 *        u_rules fully.
873
 * @return
874
 *  - 0 no bytes copied.
875
 *  - >0 the number of bytes copied.
876
 */
877
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len,
878
           fr_sbuff_term_t const *tt,
879
           fr_sbuff_unescape_rules_t const *u_rules)
880
20.0k
{
881
20.0k
  fr_sbuff_t  our_in = FR_SBUFF_BIND_CURRENT(in);
882
20.0k
  bool    do_escape = false;    /* Track state across extensions */
883
884
20.0k
  uint8_t   idx[SBUFF_CHAR_CLASS];    /* Fast path index */
885
20.0k
  size_t    needle_len = 1;
886
20.0k
  char    escape_chr = u_rules ? u_rules->chr : '\0';
887
888
20.0k
  CHECK_SBUFF_INIT(in);
889
890
  /*
891
   *  Initialise the fastpath index and
892
   *  figure out the longest needle.
893
   */
894
20.0k
  fr_sbuff_terminal_idx_init(&needle_len, idx, tt);
895
896
20.4k
  while (fr_sbuff_used_total(&our_in) < len) {
897
20.4k
    char  *p;
898
20.4k
    char  *end;
899
900
20.4k
    if (fr_sbuff_extend_lowat(NULL, &our_in, needle_len) == 0) break;
901
902
20.0k
    p = fr_sbuff_current(&our_in);
903
20.0k
    end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in));
904
905
20.0k
    if (p == end) break;
906
907
20.0k
    if (escape_chr == '\0') {
908
25.0M
      while ((p < end) && !fr_sbuff_terminal_search(in, p, idx, tt, needle_len)) p++;
909
20.0k
    } else {
910
0
      while (p < end) {
911
0
        if (do_escape) {
912
0
          do_escape = false;
913
0
        } else if (*p == escape_chr) {
914
0
          do_escape = true;
915
0
        } else if (fr_sbuff_terminal_search(in, p, idx, tt, needle_len)) {
916
0
          break;
917
0
        }
918
0
        p++;
919
0
      }
920
0
    }
921
922
20.0k
    FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p);
923
924
20.0k
    if (p != end) break;   /* stopped early, break */
925
20.0k
  }
926
927
20.0k
done:
928
20.0k
  *out->p = '\0';
929
20.0k
  return fr_sbuff_used_total(&our_in);
930
20.0k
}
931
932
/** Copy as many allowed characters as possible from a sbuff to a sbuff
933
 *
934
 * Copy size is limited by available data in sbuff and output buffer length.
935
 *
936
 * As soon as a disallowed character is found the copy is stopped.
937
 * The input sbuff will be left pointing at the first disallowed character.
938
 *
939
 * This de-escapes characters as they're copied out of the sbuff.
940
 *
941
 * @param[out] out    Where to copy to.
942
 * @param[in] in    Where to copy from.  Will copy len bytes from current position in buffer.
943
 * @param[in] len   How many bytes to copy.  If SIZE_MAX the entire buffer will be copied.
944
 * @param[in] tt    Token terminal strings in the encompassing grammar.
945
 * @param[in] u_rules   for processing unescape sequences.
946
 * @return
947
 *  - 0 no bytes copied.
948
 *  - >0 the number of bytes written to out.
949
 */
950
size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len,
951
           fr_sbuff_term_t const *tt,
952
           fr_sbuff_unescape_rules_t const *u_rules)
953
36.1k
{
954
36.1k
  fr_sbuff_t      our_in;
955
36.1k
  bool        do_escape = false;      /* Track state across extensions */
956
36.1k
  fr_sbuff_marker_t   o_s;
957
36.1k
  fr_sbuff_marker_t   c_s;
958
36.1k
  fr_sbuff_marker_t   end;
959
960
36.1k
  uint8_t       idx[SBUFF_CHAR_CLASS];      /* Fast path index */
961
36.1k
  size_t        needle_len = 1;
962
36.1k
  fr_sbuff_extend_status_t  status = 0;
963
964
  /*
965
   *  If we don't need to do unescaping
966
   *  call a more suitable function.
967
   */
968
36.1k
  if (!u_rules || (u_rules->chr == '\0')) return fr_sbuff_out_bstrncpy_until(out, in, len, tt, u_rules);
969
970
16.0k
  CHECK_SBUFF_INIT(in);
971
972
16.0k
  our_in = FR_SBUFF(in);
973
974
  /*
975
   *  Chunk tracking...
976
   */
977
16.0k
  fr_sbuff_marker(&c_s, &our_in);
978
16.0k
  fr_sbuff_marker(&end, &our_in);
979
16.0k
  fr_sbuff_marker_update_end(&end, len);
980
981
16.0k
  fr_sbuff_marker(&o_s, out);
982
983
  /*
984
   *  Initialise the fastpath index and
985
   *  figure out the longest needle.
986
   */
987
16.0k
  fr_sbuff_terminal_idx_init(&needle_len, idx, tt);
988
989
  /*
990
   *  ...while we have remaining data
991
   */
992
124k
  while (fr_sbuff_extend_lowat(&status, &our_in, needle_len) > 0) {
993
124k
    if (fr_sbuff_was_extended(status)) fr_sbuff_marker_update_end(&end, len);
994
124k
    if (!fr_sbuff_diff(&our_in, &end)) break; /* Reached the end */
995
996
124k
    if (do_escape) {
997
1.07k
      do_escape = false;
998
999
      /*
1000
       *  Check for \x<hex><hex>
1001
       */
1002
1.07k
      if (u_rules->do_hex && fr_sbuff_is_char(&our_in, 'x')) {
1003
319
        uint8_t     escape;
1004
319
        fr_sbuff_marker_t m;
1005
1006
319
        fr_sbuff_marker(&m, &our_in);   /* allow for backtrack */
1007
319
        fr_sbuff_advance(&our_in, 1);   /* skip over the 'x' */
1008
1009
319
        if (fr_sbuff_out_uint8_hex(NULL, &escape, &our_in, false) != 2) {
1010
55
          fr_sbuff_set(&our_in, &m);  /* backtrack */
1011
55
          fr_sbuff_marker_release(&m);
1012
55
          goto check_subs;    /* allow sub for \x */
1013
55
        }
1014
1015
264
        if (fr_sbuff_in_char(out, escape) <= 0) {
1016
0
          fr_sbuff_set(&our_in, &m);  /* backtrack */
1017
0
          fr_sbuff_marker_release(&m);
1018
0
          break;
1019
0
        }
1020
264
        fr_sbuff_marker_release(&m);
1021
264
        fr_sbuff_set(&c_s, &our_in);
1022
264
        continue;
1023
264
      }
1024
1025
      /*
1026
       *  Check for \<oct><oct><oct>
1027
       */
1028
757
      if (u_rules->do_oct && fr_sbuff_is_digit(&our_in)) {
1029
578
        uint8_t     escape;
1030
578
        fr_sbuff_marker_t m;
1031
1032
578
        fr_sbuff_marker(&m, &our_in);   /* allow for backtrack */
1033
1034
578
        if (fr_sbuff_out_uint8_oct(NULL, &escape, &our_in, false) != 3) {
1035
535
          fr_sbuff_set(&our_in, &m);  /* backtrack */
1036
535
          fr_sbuff_marker_release(&m);
1037
535
          goto check_subs;    /* allow sub for \<oct> */
1038
535
        }
1039
1040
43
        if (fr_sbuff_in_char(out, escape) <= 0) {
1041
0
          fr_sbuff_set(&our_in, &m);  /* backtrack */
1042
0
          fr_sbuff_marker_release(&m);
1043
0
          break;
1044
0
        }
1045
43
        fr_sbuff_marker_release(&m);
1046
43
        fr_sbuff_set(&c_s, &our_in);
1047
43
        continue;
1048
43
      }
1049
1050
769
    check_subs:
1051
      /*
1052
       *  Not a recognised hex or octal escape sequence
1053
       *  may be a substitution or a sequence that
1054
       *  should be copied to the output buffer.
1055
       */
1056
769
      {
1057
769
        uint8_t c = *fr_sbuff_current(&our_in);
1058
1059
769
        if (u_rules->subs[c] == '\0') {
1060
769
          if (u_rules->skip[c] == true) goto next;
1061
737
          goto next_esc;
1062
769
        }
1063
1064
        /*
1065
         *    We already copied everything up
1066
         *  to this point, so we can now
1067
         *  write the substituted char to
1068
         *  the output buffer.
1069
         */
1070
0
        if (fr_sbuff_in_char(out, u_rules->subs[c]) <= 0) break;
1071
1072
        /*
1073
         *  ...and advance past the entire
1074
         *  escape seq in the input buffer.
1075
         */
1076
0
        fr_sbuff_advance(&our_in, 1);
1077
0
        fr_sbuff_set(&c_s, &our_in);
1078
0
        continue;
1079
0
      }
1080
0
    }
1081
1082
124k
  next_esc:
1083
124k
    if (*fr_sbuff_current(&our_in) == u_rules->chr) {
1084
      /*
1085
       *  Copy out any data we got before
1086
       *  we hit the escape char.
1087
       *
1088
       *  We need to do this before we
1089
       *  can write the escape char to
1090
       *  the output sbuff.
1091
       */
1092
1.07k
      FILL_OR_GOTO_DONE(out, &c_s, fr_sbuff_behind(&c_s));
1093
1094
1.07k
      do_escape = true;
1095
1.07k
      fr_sbuff_advance(&our_in, 1);
1096
1.07k
      continue;
1097
1.07k
    }
1098
1099
123k
  next:
1100
123k
    if (tt && fr_sbuff_terminal_search(&our_in, fr_sbuff_current(&our_in), idx, tt, needle_len)) break;
1101
107k
    fr_sbuff_advance(&our_in, 1);
1102
107k
  }
1103
1104
  /*
1105
   *  Copy any remaining data over
1106
   */
1107
16.0k
  FILL_OR_GOTO_DONE(out, &c_s, fr_sbuff_behind(&c_s));
1108
1109
16.0k
done:
1110
16.0k
  fr_sbuff_set(in, &c_s); /* Only advance by as much as we copied */
1111
16.0k
  *out->p = '\0';
1112
1113
16.0k
  return fr_sbuff_marker_release_behind(&o_s);
1114
16.0k
}
1115
1116
/** See if the string contains a truth value
1117
 *
1118
 * @param[out] out  Where to write boolean value.
1119
 * @param[in] in  Where to search for a truth value.
1120
 * @return
1121
 *  - >0 the number of bytes consumed.
1122
 *  - -1 no bytes copied, was not a truth value.
1123
 */
1124
fr_slen_t fr_sbuff_out_bool(bool *out, fr_sbuff_t *in)
1125
20.5k
{
1126
20.5k
  fr_sbuff_t our_in = FR_SBUFF(in);
1127
1128
20.5k
  static bool const bool_prefix[SBUFF_CHAR_CLASS] = {
1129
20.5k
    ['t'] = true, ['T'] = true, /* true */
1130
20.5k
    ['f'] = true, ['F'] = true, /* false */
1131
20.5k
    ['y'] = true, ['Y'] = true, /* yes */
1132
20.5k
    ['n'] = true, ['N'] = true, /* no */
1133
20.5k
  };
1134
1135
20.5k
  if (fr_sbuff_is_in_charset(&our_in, bool_prefix)) {
1136
669
    switch (tolower(fr_sbuff_uint8(&our_in, '\0'))) {
1137
0
    default:
1138
0
      break;
1139
1140
12
    case 't':
1141
12
      if (fr_sbuff_adv_past_strcase_literal(&our_in, "true")) {
1142
2
        *out = true;
1143
2
        FR_SBUFF_SET_RETURN(in, &our_in);
1144
2
      }
1145
10
      break;
1146
1147
50
    case 'f':
1148
50
      if (fr_sbuff_adv_past_strcase_literal(&our_in, "false")) {
1149
30
        *out = false;
1150
30
        FR_SBUFF_SET_RETURN(in, &our_in);
1151
30
      }
1152
20
      break;
1153
1154
20
    case 'y':
1155
4
      if (fr_sbuff_adv_past_strcase_literal(&our_in, "yes")) {
1156
2
        *out = true;
1157
2
        FR_SBUFF_SET_RETURN(in, &our_in);
1158
2
      }
1159
2
      break;
1160
1161
603
    case 'n':
1162
603
      if (fr_sbuff_adv_past_strcase_literal(&our_in, "no")) {
1163
3
        *out = false;
1164
3
        FR_SBUFF_SET_RETURN(in, &our_in);
1165
3
      }
1166
600
      break;
1167
669
    }
1168
669
  }
1169
1170
20.4k
  *out = false; /* Always initialise out */
1171
1172
20.4k
  fr_strerror_const("Not a valid boolean value.  Accepted values are 'yes', 'no', 'true', 'false'");
1173
1174
20.4k
  return -1;
1175
20.5k
}
1176
1177
/** Used to define a number parsing functions for signed integers
1178
 *
1179
 * @param[in] _name Function suffix.
1180
 * @param[in] _type Output type.
1181
 * @param[in] _min  value.
1182
 * @param[in] _max  value.
1183
 * @param[in] _max_char Maximum digits that can be used to represent an integer.
1184
 *      Can't use stringify because of width modifiers like 'u'
1185
 *      used in <stdint.h>.
1186
 * @param[in] _base to use.
1187
 */
1188
#define SBUFF_PARSE_INT_DEF(_name, _type, _min, _max, _max_char, _base) \
1189
14.4k
fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
1190
14.4k
{ \
1191
14.4k
  char    buff[_max_char + 1]; \
1192
14.4k
  char    *end, *a_end; \
1193
14.4k
  size_t    len; \
1194
14.4k
  long long num; \
1195
14.4k
  _type   cast_num; \
1196
14.4k
  fr_sbuff_t  our_in = FR_SBUFF(in); \
1197
14.4k
  buff[0] = '\0'; /* clang scan */ \
1198
14.4k
  len = fr_sbuff_out_bstrncpy(&FR_SBUFF_IN(buff, sizeof(buff)), &our_in, _max_char); \
1199
14.4k
  if (len == 0) { \
1200
15
    if (err) *err = (fr_sbuff_remaining(in) == 0) ? FR_SBUFF_PARSE_ERROR_INPUT_EMPTY : FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
1201
15
    return -1; \
1202
15
  } \
1203
14.4k
  errno = 0; /* this is needed as strtoll doesn't reset errno */ \
1204
14.4k
  num = strtoll(buff, &end, _base); \
1205
14.4k
  cast_num = (_type)(num); \
1206
14.4k
  if (end == buff) { \
1207
939
    if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
1208
939
    return -1; \
1209
939
  } \
1210
14.4k
  if (num > cast_num) { \
1211
77
  overflow: \
1212
77
    if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
1213
77
    *out = (_type)(_max); \
1214
77
    return -1; \
1215
51
  } \
1216
13.5k
  if (((errno == EINVAL) && (num == 0)) || ((errno == ERANGE) && (num == LLONG_MAX))) goto overflow; \
1217
13.4k
  if (num < cast_num) { \
1218
437
  underflow: \
1219
437
    if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
1220
437
    *out = (_type)(_min); \
1221
437
    return -1; \
1222
18
  } \
1223
13.4k
  if ((errno == ERANGE) && (num == LLONG_MIN)) goto underflow; \
1224
13.4k
  if (no_trailing && ((a_end = in->p + (end - buff)) < in->end)) { \
1225
12.4k
    if (isdigit((uint8_t) *a_end) || (((_base > 10) || ((_base == 0) && (len > 2) && (buff[0] == '0') && (buff[1] == 'x'))) && \
1226
12.1k
        ((tolower((uint8_t) *a_end) >= 'a') && (tolower((uint8_t) *a_end) <= 'f')))) { \
1227
316
      if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
1228
316
      *out = (_type)(_max); \
1229
316
      FR_SBUFF_ERROR_RETURN(&our_in); \
1230
316
    } \
1231
12.4k
    *out = cast_num; \
1232
12.1k
  } else { \
1233
548
    if (err) *err = FR_SBUFF_PARSE_OK; \
1234
548
    *out = cast_num; \
1235
548
  } \
1236
13.0k
  return fr_sbuff_advance(in, end - buff); /* Advance by the length strtoll gives us */ \
1237
13.0k
}
1238
1239
199
SBUFF_PARSE_INT_DEF(int8, int8_t, INT8_MIN, INT8_MAX, 4, 0)
1240
630
SBUFF_PARSE_INT_DEF(int16, int16_t, INT16_MIN, INT16_MAX, 6, 0)
1241
185
SBUFF_PARSE_INT_DEF(int32, int32_t, INT32_MIN, INT32_MAX, 11, 0)
1242
13.4k
SBUFF_PARSE_INT_DEF(int64, int64_t, INT64_MIN, INT64_MAX, 20, 0)
1243
0
SBUFF_PARSE_INT_DEF(ssize, ssize_t, SSIZE_MIN, SSIZE_MAX, 20, 0)
1244
1245
/** Used to define a number parsing functions for signed integers
1246
 *
1247
 * @param[in] _name Function suffix.
1248
 * @param[in] _type Output type.
1249
 * @param[in] _max  value.
1250
 * @param[in] _max_char Maximum digits that can be used to represent an integer.
1251
 *      Can't use stringify because of width modifiers like 'u'
1252
 *      used in <stdint.h>.
1253
 * @param[in] _base of the number being parsed, 8, 10, 16 etc...
1254
 */
1255
#define SBUFF_PARSE_UINT_DEF(_name, _type, _max, _max_char, _base) \
1256
135k
fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
1257
135k
{ \
1258
135k
  char      buff[_max_char + 1]; \
1259
135k
  char      *end, *a_end; \
1260
135k
  size_t      len; \
1261
135k
  unsigned long long  num; \
1262
135k
  _type     cast_num; \
1263
135k
  fr_sbuff_t    our_in = FR_SBUFF(in); \
1264
135k
  buff[0] = '\0'; /* clang scan */ \
1265
135k
  len = fr_sbuff_out_bstrncpy(&FR_SBUFF_IN(buff, sizeof(buff)), &our_in, _max_char); \
1266
135k
  if (len == 0) { \
1267
126
    if (err) *err = (fr_sbuff_remaining(in) == 0) ? FR_SBUFF_PARSE_ERROR_INPUT_EMPTY : FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
1268
126
    return -1; \
1269
126
  } \
1270
135k
  if (buff[0] == '-') { \
1271
4.92k
    if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
1272
4.92k
    return -1; \
1273
4.92k
  } \
1274
135k
  errno = 0; /* this is needed as strtoull doesn't reset errno */ \
1275
130k
  num = strtoull(buff, &end, _base); \
1276
130k
  cast_num = (_type)(num); \
1277
130k
  if (end == buff) { \
1278
19.1k
    if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
1279
19.1k
    return -1; \
1280
19.1k
  } \
1281
130k
  if (num > cast_num) { \
1282
5.05k
  overflow: \
1283
5.05k
    if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
1284
5.05k
    *out = (_type)(_max); \
1285
5.05k
    return -1; \
1286
4.45k
  } \
1287
111k
  if (((errno == EINVAL) && (num == 0)) || ((errno == ERANGE) && (num == ULLONG_MAX))) goto overflow; \
1288
107k
  if (no_trailing && ((a_end = in->p + (end - buff)) < in->end)) { \
1289
34.3k
    if (isdigit((uint8_t) *a_end) || (((_base > 10) || ((_base == 0) && (len > 2) && (buff[0] == '0') && (buff[1] == 'x'))) && \
1290
33.0k
        ((tolower((uint8_t) *a_end) >= 'a') && (tolower((uint8_t) *a_end) <= 'f')))) { \
1291
1.47k
      if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
1292
1.47k
      *out = (_type)(_max); \
1293
1.47k
      FR_SBUFF_ERROR_RETURN(&our_in); \
1294
1.47k
    } \
1295
34.3k
    if (err) *err = FR_SBUFF_PARSE_OK; \
1296
32.8k
    *out = cast_num; \
1297
72.4k
  } else { \
1298
72.4k
    if (err) *err = FR_SBUFF_PARSE_OK; \
1299
72.4k
    *out = cast_num; \
1300
72.4k
  } \
1301
106k
  return fr_sbuff_advance(in, end - buff); /* Advance by the length strtoull gives us */ \
1302
106k
}
1303
1304
/* max chars here is the octal string value with prefix */
1305
45.6k
SBUFF_PARSE_UINT_DEF(uint8, uint8_t, UINT8_MAX, 4, 0)
1306
3.48k
SBUFF_PARSE_UINT_DEF(uint16, uint16_t, UINT16_MAX, 7, 0)
1307
65.8k
SBUFF_PARSE_UINT_DEF(uint32, uint32_t, UINT32_MAX, 12, 0)
1308
20.0k
SBUFF_PARSE_UINT_DEF(uint64, uint64_t, UINT64_MAX, 23, 0)
1309
0
SBUFF_PARSE_UINT_DEF(size, size_t, SIZE_MAX, 23, 0)
1310
1311
0
SBUFF_PARSE_UINT_DEF(uint8_dec, uint8_t, UINT8_MAX, 3, 0)
1312
0
SBUFF_PARSE_UINT_DEF(uint16_dec, uint16_t, UINT16_MAX, 4, 0)
1313
0
SBUFF_PARSE_UINT_DEF(uint32_dec, uint32_t, UINT32_MAX, 10, 0)
1314
0
SBUFF_PARSE_UINT_DEF(uint64_dec, uint64_t, UINT64_MAX, 19, 0)
1315
0
SBUFF_PARSE_UINT_DEF(size_dec, size_t, SIZE_MAX, 19, 0)
1316
1317
1318
578
SBUFF_PARSE_UINT_DEF(uint8_oct, uint8_t, UINT8_MAX, 3, 8)
1319
0
SBUFF_PARSE_UINT_DEF(uint16_oct, uint16_t, UINT16_MAX, 6, 8)
1320
0
SBUFF_PARSE_UINT_DEF(uint32_oct, uint32_t, UINT32_MAX, 11, 8)
1321
0
SBUFF_PARSE_UINT_DEF(uint64_oct, uint64_t, UINT64_MAX, 22, 8)
1322
0
SBUFF_PARSE_UINT_DEF(size_oct, size_t, SIZE_MAX, 22, 8)
1323
1324
319
SBUFF_PARSE_UINT_DEF(uint8_hex, uint8_t, UINT8_MAX, 2, 16)
1325
0
SBUFF_PARSE_UINT_DEF(uint16_hex, uint16_t, UINT16_MAX, 4, 16)
1326
0
SBUFF_PARSE_UINT_DEF(uint32_hex, uint32_t, UINT32_MAX, 8, 16)
1327
0
SBUFF_PARSE_UINT_DEF(uint64_hex, uint64_t, UINT64_MAX, 16, 16)
1328
0
SBUFF_PARSE_UINT_DEF(size_hex, size_t, SIZE_MAX, 22, 16)
1329
1330
/** Used to define a number parsing functions for floats
1331
 *
1332
 * @param[in] _name Function suffix.
1333
 * @param[in] _type Output type.
1334
 * @param[in] _func Parsing function to use.
1335
 * @param[in] _max_char Maximum digits that can be used to represent an integer.
1336
 *      Can't use stringify because of width modifiers like 'u'
1337
 *      used in <stdint.h>.
1338
 */
1339
#define SBUFF_PARSE_FLOAT_DEF(_name, _type, _func, _max_char) \
1340
4.63k
fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \
1341
4.63k
{ \
1342
4.63k
  char    buff[_max_char + 1] = ""; \
1343
4.63k
  char    *end; \
1344
4.63k
  fr_sbuff_t  our_in = FR_SBUFF(in); \
1345
4.63k
  size_t    len; \
1346
4.63k
  _type   res; \
1347
4.63k
  len = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(buff, sizeof(buff)), &our_in, SIZE_MAX, sbuff_char_class_float); \
1348
4.63k
  if (len == sizeof(buff)) { \
1349
0
    if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
1350
0
    return -1; \
1351
4.63k
  } else if (len == 0) { \
1352
851
    if (err) *err = (fr_sbuff_remaining(in) == 0) ? FR_SBUFF_PARSE_ERROR_INPUT_EMPTY : FR_SBUFF_PARSE_ERROR_NOT_FOUND; \
1353
851
    return -1; \
1354
851
  } \
1355
4.63k
  errno = 0; /* this is needed as parsing functions don't reset errno */ \
1356
3.78k
  res = _func(buff, &end); \
1357
3.78k
  if (errno == ERANGE) { \
1358
146
    if (res > 0) { \
1359
122
      if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \
1360
122
    } else { \
1361
24
      if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \
1362
24
    } \
1363
146
    return -1; \
1364
146
  } \
1365
3.78k
  if (no_trailing && (*end != '\0')) { \
1366
254
    if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \
1367
254
    FR_SBUFF_ERROR_RETURN(&our_in); \
1368
254
  } \
1369
3.63k
  *out = res; \
1370
3.38k
  return fr_sbuff_advance(in, end - buff); \
1371
3.63k
}
1372
1373
138
SBUFF_PARSE_FLOAT_DEF(float32, float, strtof, 100)
1374
4.49k
SBUFF_PARSE_FLOAT_DEF(float64, double, strtod, 100)
1375
1376
/** Move data from one sbuff to another
1377
 *
1378
 * @note Do not call this function directly use #fr_sbuff_move
1379
 *
1380
 * Both in and out will be advanced by len, with len set to the shortest
1381
 * value between the user specified value, the number of bytes remaining
1382
 * in the input buffer (after extension), and the number of bytes remaining
1383
 * in the output buffer (after extension).
1384
 *
1385
 * @param[in] out sbuff to copy data to.
1386
 * @param[in] in  sbuff to copy data from.
1387
 * @param[in] len Maximum length of string to copy.
1388
 * @return The amount of data copied.
1389
 */
1390
size_t _fr_sbuff_move_sbuff_to_sbuff(fr_sbuff_t *out, fr_sbuff_t *in, size_t len)
1391
193k
{
1392
193k
  size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len);
1393
193k
  size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len);
1394
193k
  size_t to_copy = len;
1395
193k
  if (to_copy > o_remaining) to_copy = o_remaining;
1396
193k
  if (to_copy > i_remaining) to_copy = i_remaining;
1397
193k
  safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy);
1398
193k
  return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy));
1399
193k
}
1400
1401
/** Move data from a marker to an sbuff
1402
 *
1403
 * @note Do not call this function directly use #fr_sbuff_move
1404
 *
1405
 * @param[in] out sbuff to copy data to.
1406
 * @param[in] in  marker to copy data from.
1407
 * @param[in] len Maximum length of string to copy.
1408
 * @return The amount of data copied.
1409
 */
1410
size_t _fr_sbuff_move_marker_to_sbuff(fr_sbuff_t *out, fr_sbuff_marker_t *in, size_t len)
1411
17.1k
{
1412
17.1k
  size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len);
1413
17.1k
  size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len);
1414
17.1k
  size_t to_copy = len;
1415
17.1k
  if (to_copy > o_remaining) to_copy = o_remaining;
1416
17.1k
  if (to_copy > i_remaining) to_copy = i_remaining;
1417
17.1k
  safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy);
1418
17.1k
  return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy));
1419
17.1k
}
1420
1421
/** Move data from one marker to another
1422
 *
1423
 * @note Do not call this function directly use #fr_sbuff_move
1424
 *
1425
 * @param[in] out marker to copy data to.
1426
 * @param[in] in  marker to copy data from.
1427
 * @param[in] len Maximum length of string to copy.
1428
 * @return The amount of data copied.
1429
 */
1430
size_t _fr_sbuff_move_marker_to_marker(fr_sbuff_marker_t *out, fr_sbuff_marker_t *in, size_t len)
1431
0
{
1432
0
  size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len);
1433
0
  size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len);
1434
0
  size_t to_copy = len;
1435
0
  if (to_copy > o_remaining) to_copy = o_remaining;
1436
0
  if (to_copy > i_remaining) to_copy = i_remaining;
1437
0
  safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy);
1438
0
  return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy));
1439
0
}
1440
1441
/** Move data from an sbuff to a marker
1442
 *
1443
 * @note Do not call this function directly use #fr_sbuff_move
1444
 *
1445
 * @param[in] out marker to copy data to.
1446
 * @param[in] in  sbuff to copy data from.
1447
 * @param[in] len Maximum length of string to copy.
1448
 * @return The amount of data copied.
1449
 */
1450
size_t _fr_sbuff_move_sbuff_to_marker(fr_sbuff_marker_t *out, fr_sbuff_t *in, size_t len)
1451
0
{
1452
0
  size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len);
1453
0
  size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len);
1454
0
  size_t to_copy = len;
1455
0
  if (to_copy > o_remaining) to_copy = o_remaining;
1456
0
  if (to_copy > i_remaining) to_copy = i_remaining;
1457
0
  safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy);
1458
0
  return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy));
1459
0
}
1460
1461
/** Copy bytes into the sbuff up to the first \0
1462
 *
1463
 * @param[in] sbuff to copy into.
1464
 * @param[in] str to copy into buffer.
1465
 * @return
1466
 *  - >= 0 the number of bytes copied into the sbuff.
1467
 *  - <0 the number of bytes required to complete the copy operation.
1468
 */
1469
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
1470
14.2k
{
1471
14.2k
  size_t len;
1472
1473
14.2k
  CHECK_SBUFF_WRITEABLE(sbuff);
1474
1475
14.2k
  len = strlen(str);
1476
14.2k
  FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len);
1477
1478
10.9k
  safecpy(sbuff->p, sbuff->end, str, str + len);
1479
10.9k
  sbuff->p[len] = '\0';
1480
1481
10.9k
  return fr_sbuff_advance(sbuff, len);
1482
14.2k
}
1483
1484
/** Copy bytes into the sbuff up to the first \0
1485
 *
1486
 * @param[in] sbuff to copy into.
1487
 * @param[in] str to copy into buffer.
1488
 * @param[in] len number of bytes to copy.
1489
 * @return
1490
 *  - >= 0 the number of bytes copied into the sbuff.
1491
 *  - <0 the number of bytes required to complete the copy operation.
1492
 */
1493
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
1494
20.5M
{
1495
20.5M
  CHECK_SBUFF_WRITEABLE(sbuff);
1496
1497
20.5M
  FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len);
1498
1499
20.4M
  safecpy(sbuff->p, sbuff->end, str, str + len);
1500
20.4M
  sbuff->p[len] = '\0';
1501
1502
20.4M
  return fr_sbuff_advance(sbuff, len);
1503
20.5M
}
1504
1505
/** Copy bytes into the sbuff up to the first \0
1506
 *
1507
 * @param[in] sbuff to copy into.
1508
 * @param[in] str talloced buffer to copy into sbuff.
1509
 * @return
1510
 *  - >= 0 the number of bytes copied into the sbuff.
1511
 *  - <0 the number of bytes required to complete the copy operation.
1512
 */
1513
ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
1514
21.2M
{
1515
21.2M
  size_t len;
1516
1517
21.2M
  CHECK_SBUFF_WRITEABLE(sbuff);
1518
1519
21.2M
  len = talloc_strlen(str);
1520
1521
21.2M
  FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len);
1522
1523
21.2M
  safecpy(sbuff->p, sbuff->end, str, str + len);
1524
21.2M
  sbuff->p[len] = '\0';
1525
1526
21.2M
  return fr_sbuff_advance(sbuff, len);
1527
21.2M
}
1528
1529
/** Free the scratch buffer used for printf
1530
 *
1531
 */
1532
static int _sbuff_scratch_free(void *arg)
1533
30
{
1534
30
  sbuff_scratch_freed = true;
1535
30
  return talloc_free(arg);
1536
30
}
1537
1538
static inline CC_HINT(always_inline) int sbuff_scratch_init(TALLOC_CTX **out)
1539
21.2M
{
1540
21.2M
  TALLOC_CTX  *scratch;
1541
1542
  /*
1543
   *  Once main has signalled shutdown the TLS slot may be a
1544
   *  dangling pointer on threads we don't own; skip the scratch
1545
   *  cache and let callers allocate at top level instead.  The
1546
   *  TLS-local `sbuff_scratch_freed` is left in place for the
1547
   *  per-thread teardown path on FR-managed threads.
1548
   */
1549
21.2M
  if (sbuff_scratch_freed || fr_atexit_thread_local_alloc_disabled()) {
1550
0
    *out = NULL;
1551
0
    return 0;
1552
0
  }
1553
1554
21.2M
  scratch = sbuff_scratch;
1555
21.2M
  if (!scratch) {
1556
30
    scratch = talloc_pool(NULL, 4096);
1557
30
    if (unlikely(!scratch)) {
1558
0
      fr_strerror_const("Out of Memory");
1559
0
      return -1;
1560
0
    }
1561
30
    fr_atexit_thread_local(sbuff_scratch, _sbuff_scratch_free, scratch);
1562
30
  }
1563
1564
21.2M
  *out = scratch;
1565
1566
21.2M
  return 0;
1567
21.2M
}
1568
1569
/** Print using a fmt string to an sbuff
1570
 *
1571
 * @param[in] sbuff to print into.
1572
 * @param[in] fmt string.
1573
 * @param[in] ap  arguments for format string.
1574
< * @return
1575
 *  - >= 0 the number of bytes printed into the sbuff.
1576
 *  - <0 the number of bytes required to complete the print operation.
1577
 */
1578
ssize_t fr_sbuff_in_vsprintf(fr_sbuff_t *sbuff, char const *fmt, va_list ap)
1579
21.2M
{
1580
21.2M
  TALLOC_CTX  *scratch;
1581
21.2M
  va_list   ap_p;
1582
21.2M
  char    *tmp;
1583
21.2M
  ssize_t   slen;
1584
1585
21.2M
  CHECK_SBUFF_WRITEABLE(sbuff);
1586
1587
21.2M
  if (sbuff_scratch_init(&scratch) < 0) return 0;
1588
1589
21.2M
  va_copy(ap_p, ap);
1590
21.2M
  tmp = fr_vasprintf(scratch, fmt, ap_p);
1591
21.2M
  va_end(ap_p);
1592
21.2M
  if (!tmp) return 0;
1593
1594
21.2M
  slen = fr_sbuff_in_bstrcpy_buffer(sbuff, tmp);
1595
21.2M
  talloc_free(tmp); /* Free the temporary buffer */
1596
1597
21.2M
  return slen;
1598
21.2M
}
1599
1600
/** Print using a fmt string to an sbuff
1601
 *
1602
 * @param[in] sbuff to print into.
1603
 * @param[in] fmt string.
1604
 * @param[in] ... arguments for format string.
1605
 * @return
1606
 *  - >= 0 the number of bytes printed into the sbuff.
1607
 *  - <0 the number of bytes required to complete the print operation.
1608
 */
1609
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt, ...)
1610
21.2M
{
1611
21.2M
  va_list   ap;
1612
21.2M
  ssize_t   slen;
1613
1614
21.2M
  CHECK_SBUFF_WRITEABLE(sbuff);
1615
1616
21.2M
  va_start(ap, fmt);
1617
21.2M
  slen = fr_sbuff_in_vsprintf(sbuff, fmt, ap);
1618
21.2M
  va_end(ap);
1619
1620
21.2M
  return slen;
1621
21.2M
}
1622
1623
/** Print an escaped string to an sbuff
1624
 *
1625
 * @param[in] sbuff to print into.
1626
 * @param[in] in  to escape.
1627
 * @param[in] inlen of string to escape.
1628
 * @param[in] e_rules Escaping rules.  Used to escape special characters
1629
 *          as data is written to the sbuff.  May be NULL.
1630
 * @return
1631
 *  - >= 0 the number of bytes printed into the sbuff.
1632
 *  - <0 the number of bytes required to complete the print operation.
1633
 */
1634
ssize_t fr_sbuff_in_escape(fr_sbuff_t *sbuff, char const *in, size_t inlen, fr_sbuff_escape_rules_t const *e_rules)
1635
13.0k
{
1636
13.0k
  char const  *end = in + inlen;
1637
13.0k
  char const  *p = in;
1638
13.0k
  fr_sbuff_t  our_sbuff;
1639
1640
  /* Significantly quicker if there are no rules */
1641
13.0k
  if (!e_rules || (e_rules->chr == '\0')) return fr_sbuff_in_bstrncpy(sbuff, in, inlen);
1642
1643
7.97k
  CHECK_SBUFF_WRITEABLE(sbuff);
1644
1645
7.97k
  our_sbuff = FR_SBUFF(sbuff);
1646
39.2M
  while (p < end) {
1647
39.2M
    size_t  clen;
1648
39.2M
    uint8_t c = (uint8_t)*p;
1649
39.2M
    char  sub;
1650
1651
    /*
1652
     *  We don't support escaping UTF8 sequences
1653
     *  as they're not used anywhere in our
1654
     *  grammar.
1655
     */
1656
39.2M
    if (e_rules->do_utf8 && ((clen = fr_utf8_char((uint8_t const *)p, end - p)) > 1)) {
1657
164k
      FR_SBUFF_IN_BSTRNCPY_RETURN(&our_sbuff, p, clen);
1658
164k
      p += clen;
1659
164k
      continue;
1660
164k
    }
1661
1662
    /*
1663
     *  Check if there's a special substitution
1664
     *  like 0x0a -> \n.
1665
     */
1666
39.0M
    sub = e_rules->subs[c];
1667
39.0M
    if (sub != '\0') {
1668
432k
      FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, e_rules->chr, sub);
1669
432k
      p++;
1670
432k
      continue;
1671
432k
    }
1672
1673
    /*
1674
     *  Check if the character is in the range
1675
     *  we escape.
1676
     */
1677
38.6M
    if (e_rules->esc[c]) {
1678
      /*
1679
       *  For legacy reasons we prefer
1680
       *  octal escape sequences.
1681
       */
1682
18.9M
      if (e_rules->do_oct) {
1683
18.9M
        FR_SBUFF_IN_SPRINTF_RETURN(&our_sbuff, "%c%03o", e_rules->chr, (uint8_t)*p++);
1684
18.9M
        continue;
1685
18.9M
      } else if (e_rules->do_hex) {
1686
0
        FR_SBUFF_IN_SPRINTF_RETURN(&our_sbuff, "%cx%02x", e_rules->chr, (uint8_t)*p++);
1687
0
        continue;
1688
0
      }
1689
18.9M
    }
1690
1691
19.7M
    FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, *p++);
1692
19.7M
  }
1693
1694
7.97k
  FR_SBUFF_SET_RETURN(sbuff, &our_sbuff);
1695
7.97k
}
1696
1697
/** Walk an input string and report whether fr_sbuff_in_escape() would
1698
 * escape any characters in it.
1699
 *
1700
 *  Mirrors the per-byte decisions of #fr_sbuff_in_escape: a byte
1701
 *  inside a multi-byte UTF-8 sequence (when do_utf8 is set) is passed
1702
 *  through, a byte with a substitution mapping is escaped, and a byte
1703
 *  in the esc[] table is escaped.  If any byte would be escaped, the
1704
 *  function returns false at that byte.  A NULL or chr=='\0' ruleset
1705
 *  is treated as "no escaping": the function always returns true.
1706
 *
1707
 * @param[in] in  to inspect.
1708
 * @param[in] inlen bytes of `in` to inspect.
1709
 * @param[in] e_rules escaping rules.  May be NULL.
1710
 * @return
1711
 *  - false at least one byte would be escaped.
1712
 *  - true  no byte would be escaped (the string is already safe).
1713
 */
1714
bool fr_sbuff_in_needs_escaping(char const *in, size_t inlen, fr_sbuff_escape_rules_t const *e_rules)
1715
0
{
1716
0
  char const  *end = in + inlen;
1717
0
  char const  *p = in;
1718
1719
0
  if (!e_rules || !e_rules->chr) return false;
1720
1721
0
  while (p < end) {
1722
0
    size_t  clen;
1723
0
    uint8_t c = (uint8_t) *p;
1724
1725
0
    if (e_rules->do_utf8 && ((clen = fr_utf8_char((uint8_t const *) p, end - p)) > 1)) {
1726
0
      p += clen;
1727
0
      continue;
1728
0
    }
1729
1730
0
    if (e_rules->subs[c] != '\0') return false;
1731
1732
0
    if (e_rules->esc[c]) return false;
1733
1734
0
    p++;
1735
0
  }
1736
1737
0
  return true;
1738
0
}
1739
1740
/** Print an escaped string to an sbuff taking a talloced buffer as input
1741
 *
1742
 * @param[in] sbuff to print into.
1743
 * @param[in] in  to escape.
1744
 * @param[in] e_rules Escaping rules.  Used to escape special characters
1745
 *          as data is written to the sbuff.  May be NULL.
1746
 * @return
1747
 *  - >= 0 the number of bytes printed into the sbuff.
1748
 *  - <0 the number of bytes required to complete the print operation.
1749
 */
1750
ssize_t fr_sbuff_in_escape_buffer(fr_sbuff_t *sbuff, char const *in, fr_sbuff_escape_rules_t const *e_rules)
1751
3.51k
{
1752
3.51k
  if (unlikely(!in)) return 0;
1753
1754
3.51k
  CHECK_SBUFF_WRITEABLE(sbuff);
1755
1756
3.51k
  return fr_sbuff_in_escape(sbuff, in, talloc_strlen(in), e_rules);
1757
3.51k
}
1758
1759
/** Concat an array of strings (NULL terminated), with a string separator
1760
 *
1761
 * @param[out] out  Where to write the resulting string.
1762
 * @param[in] array of strings to concat.
1763
 * @param[in] sep to insert between elements.  May be NULL.
1764
 * @return
1765
 *      - >= 0 on success - length of the string created.
1766
 *  - <0 on failure.  How many bytes we would need.
1767
 */
1768
fr_slen_t fr_sbuff_in_array(fr_sbuff_t *out, char const * const *array, char const *sep)
1769
0
{
1770
0
  fr_sbuff_t    our_out = FR_SBUFF(out);
1771
0
  char const * const *  p;
1772
0
  fr_sbuff_escape_rules_t e_rules = {
1773
0
          .name = __FUNCTION__,
1774
0
          .chr = '\\'
1775
0
        };
1776
1777
0
  if (sep) e_rules.subs[(uint8_t)*sep] = *sep;
1778
1779
0
  CHECK_SBUFF_WRITEABLE(out);
1780
1781
0
  for (p = array; *p; p++) {
1782
0
    if (*p) FR_SBUFF_RETURN(fr_sbuff_in_escape, &our_out, *p, strlen(*p), &e_rules);
1783
1784
0
    if (sep && p[1]) {
1785
0
      FR_SBUFF_RETURN(fr_sbuff_in_strcpy, &our_out, sep);
1786
0
    }
1787
0
  }
1788
1789
0
  FR_SBUFF_SET_RETURN(out, &our_out);
1790
0
}
1791
1792
/** Return true and advance past the end of the needle if needle occurs next in the sbuff
1793
 *
1794
 * @param[in] sbuff   to search in.
1795
 * @param[in] needle    to search for.
1796
 * @param[in] needle_len  of needle. If SIZE_MAX strlen is used
1797
 *        to determine length of the needle.
1798
 * @return how many bytes we advanced
1799
 */
1800
size_t fr_sbuff_adv_past_str(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
1801
75.5k
{
1802
75.5k
  char const *found;
1803
1804
75.5k
  CHECK_SBUFF_INIT(sbuff);
1805
1806
75.5k
  if (needle_len == SIZE_MAX) needle_len = strlen(needle);
1807
1808
  /*
1809
   *  If there's insufficient bytes in the
1810
   *  buffer currently, try to extend it,
1811
   *  returning if we can't.
1812
   */
1813
75.5k
  if (fr_sbuff_extend_lowat(NULL, sbuff, needle_len) < needle_len) return 0;
1814
1815
75.3k
  found = memmem(sbuff->p, needle_len, needle, needle_len); /* sbuff needle_len and needle needle_len ensures match must be next */
1816
75.3k
  if (!found) return 0;
1817
1818
3.06k
  return fr_sbuff_advance(sbuff, needle_len);
1819
75.3k
}
1820
1821
/** Return true and advance past the end of the needle if needle occurs next in the sbuff
1822
 *
1823
 * This function is similar to fr_sbuff_adv_past_str but is case insensitive.
1824
 *
1825
 * @param[in] sbuff   to search in.
1826
 * @param[in] needle    to search for.
1827
 * @param[in] needle_len  of needle. If SIZE_MAX strlen is used
1828
 *        to determine length of the needle.
1829
 * @return how many bytes we advanced
1830
 */
1831
size_t fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
1832
42.1k
{
1833
42.1k
  char const *p, *n_p;
1834
42.1k
  char const *end;
1835
1836
42.1k
  CHECK_SBUFF_INIT(sbuff);
1837
1838
42.1k
  if (needle_len == SIZE_MAX) needle_len = strlen(needle);
1839
1840
  /*
1841
   *  If there's insufficient bytes in the
1842
   *  buffer currently, try to extend it,
1843
   *  returning if we can't.
1844
   */
1845
42.1k
  if (fr_sbuff_extend_lowat(NULL, sbuff, needle_len) < needle_len) return 0;
1846
1847
42.0k
  p = sbuff->p;
1848
42.0k
  end = p + needle_len;
1849
1850
46.3k
  for (p = sbuff->p, n_p = needle; p < end; p++, n_p++) {
1851
45.9k
    if (tolower((uint8_t) *p) != tolower((uint8_t) *n_p)) return 0;
1852
45.9k
  }
1853
1854
440
  return fr_sbuff_advance(sbuff, needle_len);
1855
42.0k
}
1856
1857
/** Wind position past characters in the allowed set
1858
 *
1859
 * @param[in] sbuff   sbuff to search in.
1860
 * @param[in] len   Maximum amount to advance by. Unconstrained if SIZE_MAX.
1861
 * @param[in] allowed   character set.
1862
 * @param[in] tt    If not NULL, stop if we find a terminal sequence.
1863
 * @return how many bytes we advanced.
1864
 */
1865
size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool
1866
         const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt)
1867
188k
{
1868
188k
  size_t    total = 0;
1869
188k
  char const  *p;
1870
188k
  uint8_t   idx[SBUFF_CHAR_CLASS];  /* Fast path index */
1871
188k
  size_t    needle_len = 0;
1872
1873
188k
  CHECK_SBUFF_INIT(sbuff);
1874
1875
188k
  if (tt) fr_sbuff_terminal_idx_init(&needle_len, idx, tt);
1876
1877
194k
  while (total < len) {
1878
189k
    char *end;
1879
1880
189k
    if (!fr_sbuff_extend(sbuff)) break;
1881
1882
188k
    end = CONSTRAINED_END(sbuff, len, total);
1883
188k
    p = sbuff->p;
1884
16.9M
    while ((p < end) && allowed[(uint8_t)*p]) {
1885
16.7M
      if (needle_len == 0) {
1886
16.7M
        p++;
1887
16.7M
        continue;
1888
16.7M
      }
1889
1890
           /*
1891
      * If this character is allowed, BUT is also listed as a one-character terminal,
1892
      * then we still allow it.  This decision implements "greedy" parsing.
1893
      */
1894
0
           if (fr_sbuff_terminal_search(sbuff, p, idx, tt, 1)) {
1895
0
             p++;
1896
0
             continue;
1897
0
           }
1898
1899
           /*
1900
      * Otherwise if the next *set* of characters) is not in the terminals, then
1901
      * allow the current character.
1902
      */
1903
0
           if (!fr_sbuff_terminal_search(sbuff, p, idx, tt, needle_len)) {
1904
0
             p++;
1905
0
             continue;
1906
0
           }
1907
1908
           /*
1909
      * The character is allowed, and is NOT listed as a terminal character by itself.
1910
      * However, it is part of a multi-character terminal sequence.  We therefore
1911
      * stop.
1912
      *
1913
      * This decision allows us to parse things like "Framed-User", where we might
1914
      * normally stop at the "-".  However, we will still stop at "Framed-=User", as
1915
      * "-=" may be a terminal sequence.
1916
      *
1917
      * There is no perfect solution here, other than to fix the input grammar so that
1918
      * it has no ambiguity.  Since we can't do that, we choose to err on the side of
1919
      * allowing the existing grammar, where it makes sense
1920
      */
1921
0
           break;
1922
0
    }
1923
1924
188k
    total += fr_sbuff_set(sbuff, p);
1925
188k
    if (p != end) break;   /* stopped early, break */
1926
188k
  }
1927
1928
188k
  return total;
1929
188k
}
1930
1931
/** Wind position until we hit a character in the terminal set
1932
 *
1933
 * @param[in] sbuff   sbuff to search in.
1934
 * @param[in] len   Maximum amount to advance by. Unconstrained if SIZE_MAX.
1935
 * @param[in] tt    Token terminals in the encompassing grammar.
1936
 * @param[in] escape_chr  If not '\0', ignore characters in the tt set when
1937
 *        prefixed with this escape character.
1938
 * @return how many bytes we advanced.
1939
 */
1940
size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr)
1941
1.64k
{
1942
1.64k
  size_t    total = 0;
1943
1.64k
  char const  *p;
1944
1.64k
  bool    do_escape = false;    /* Track state across extensions */
1945
1946
1.64k
  uint8_t   idx[SBUFF_CHAR_CLASS];    /* Fast path index */
1947
1.64k
  size_t    needle_len = 1;
1948
1949
1.64k
  CHECK_SBUFF_INIT(sbuff);
1950
1951
  /*
1952
   *  Initialise the fastpath index and
1953
   *  figure out the longest needle.
1954
   */
1955
1.64k
  fr_sbuff_terminal_idx_init(&needle_len, idx, tt);
1956
1957
2.23k
  while (total < len) {
1958
2.10k
    char *end;
1959
1960
2.10k
    if (fr_sbuff_extend_lowat(NULL, sbuff, needle_len) == 0) break;
1961
1962
1.64k
    end = CONSTRAINED_END(sbuff, len, total);
1963
1.64k
    p = sbuff->p;
1964
1965
1.64k
    if (escape_chr == '\0') {
1966
58.4k
      while ((p < end) && !fr_sbuff_terminal_search(sbuff, p, idx, tt, needle_len)) p++;
1967
1.64k
    } else {
1968
0
      while (p < end) {
1969
0
        if (do_escape) {
1970
0
          do_escape = false;
1971
0
        } else if (*p == escape_chr) {
1972
0
          do_escape = true;
1973
0
        } else if (fr_sbuff_terminal_search(sbuff, p, idx, tt, needle_len)) {
1974
0
          break;
1975
0
        }
1976
0
        p++;
1977
0
      }
1978
0
    }
1979
1980
1.64k
    total += fr_sbuff_set(sbuff, p);
1981
1.64k
    if (p != end) break; /* stopped early, break */
1982
1.64k
  }
1983
1984
1.64k
  return total;
1985
1.64k
}
1986
1987
/** Wind position to first instance of specified multibyte utf8 char
1988
 *
1989
 * Only use this function if the search char could be multibyte,
1990
 * as there's a large performance penalty.
1991
 *
1992
 * @param[in,out] sbuff   to search in.
1993
 * @param[in] len   the maximum number of characters to search in sbuff.
1994
 * @param[in] chr   to search for.
1995
 * @return
1996
 *  - NULL, no instances found.
1997
 *  - The position of the first character.
1998
 */
1999
char *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, size_t len, char const *chr)
2000
0
{
2001
0
  fr_sbuff_t  our_sbuff = FR_SBUFF(sbuff);
2002
0
  size_t    total = 0;
2003
0
  size_t    clen = strlen(chr);
2004
2005
0
  CHECK_SBUFF_INIT(sbuff);
2006
2007
  /*
2008
   *  Needle bigger than haystack
2009
   */
2010
0
  if (len < clen) return NULL;
2011
2012
0
  while (total <= (len - clen)) {
2013
0
    char const  *found;
2014
0
    char    *end;
2015
2016
    /*
2017
     *  Ensure we have enough chars to match
2018
     *  the needle.
2019
     */
2020
0
    if (fr_sbuff_extend_lowat(NULL, &our_sbuff, clen) < clen) break;
2021
2022
0
    end = CONSTRAINED_END(&our_sbuff, len, total);
2023
2024
0
    found = fr_utf8_strchr(NULL, our_sbuff.p, end - our_sbuff.p, chr);
2025
0
    if (found) {
2026
0
      (void)fr_sbuff_set(sbuff, found);
2027
0
      return sbuff->p;
2028
0
    }
2029
0
    total += fr_sbuff_set(&our_sbuff, (end - clen) + 1);
2030
0
  }
2031
2032
0
  return NULL;
2033
0
}
2034
2035
/** Wind position to first instance of specified char
2036
 *
2037
 * @param[in,out] sbuff   to search in.
2038
 * @param[in] len   Maximum amount to advance by. Unconstrained if SIZE_MAX.
2039
 * @param[in] c     to search for.
2040
 * @return
2041
 *  - NULL, no instances found.
2042
 *  - The position of the first character.
2043
 */
2044
char *fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c)
2045
0
{
2046
0
  fr_sbuff_t  our_sbuff = FR_SBUFF(sbuff);
2047
0
  size_t    total = 0;
2048
2049
0
  CHECK_SBUFF_INIT(sbuff);
2050
2051
0
  while (total < len) {
2052
0
    char const  *found;
2053
0
    char    *end;
2054
2055
0
    if (!fr_sbuff_extend(&our_sbuff)) break;
2056
2057
0
    end = CONSTRAINED_END(&our_sbuff, len, total);
2058
0
    found = memchr(our_sbuff.p, c, end - our_sbuff.p);
2059
0
    if (found) {
2060
0
      (void)fr_sbuff_set(sbuff, found);
2061
0
      return sbuff->p;
2062
0
    }
2063
2064
0
    total += fr_sbuff_set(&our_sbuff, end);
2065
0
  }
2066
2067
0
  return NULL;
2068
0
}
2069
2070
/** Wind position to the first instance of the specified needle
2071
 *
2072
 * @param[in,out] sbuff   sbuff to search in.
2073
 * @param[in] len   Maximum amount to advance by. Unconstrained if SIZE_MAX.
2074
 * @param[in] needle    to search for.
2075
 * @param[in] needle_len  Length of the needle. SIZE_MAX to used strlen.
2076
 * @return
2077
 *  - NULL, no instances found.
2078
 *  - The position of the first character.
2079
 */
2080
char *fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len)
2081
0
{
2082
0
  fr_sbuff_t  our_sbuff = FR_SBUFF(sbuff);
2083
0
  size_t    total = 0;
2084
2085
0
  CHECK_SBUFF_INIT(sbuff);
2086
2087
0
  if (needle_len == SIZE_MAX) needle_len = strlen(needle);
2088
0
  if (!needle_len) return NULL;
2089
2090
  /*
2091
   *  Needle bigger than haystack
2092
   */
2093
0
  if (len < needle_len) return NULL;
2094
2095
0
  while (total <= (len - needle_len)) {
2096
0
    char const  *found;
2097
0
    char    *end;
2098
2099
    /*
2100
     *  If the needle is longer than
2101
     *  the remaining buffer, return.
2102
     */
2103
0
    if (fr_sbuff_extend_lowat(NULL, &our_sbuff, needle_len) < needle_len) break;
2104
2105
0
    end = CONSTRAINED_END(&our_sbuff, len, total);
2106
0
    found = memmem(our_sbuff.p, end - our_sbuff.p, needle, needle_len);
2107
0
    if (found) {
2108
0
      (void)fr_sbuff_set(sbuff, found);
2109
0
      return sbuff->p;
2110
0
    }
2111
2112
    /*
2113
     *  Partial needle may be in
2114
     *      the end of the buffer so
2115
     *  don't advance too far.
2116
     */
2117
0
    total += fr_sbuff_set(&our_sbuff, (end - needle_len) + 1);
2118
0
  }
2119
2120
0
  return NULL;
2121
0
}
2122
2123
/** Wind position to the first instance of the specified needle
2124
 *
2125
 * @param[in,out] sbuff   sbuff to search in.
2126
 * @param[in] len   Maximum amount to advance by. Unconstrained if SIZE_MAX.
2127
 * @param[in] needle    to search for.
2128
 * @param[in] needle_len  Length of the needle. SIZE_MAX to used strlen.
2129
 * @return
2130
 *  - NULL, no instances found.
2131
 *  - The position of the first character.
2132
 */
2133
char *fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len)
2134
0
{
2135
0
  fr_sbuff_t  our_sbuff = FR_SBUFF(sbuff);
2136
0
  size_t    total = 0;
2137
2138
0
  CHECK_SBUFF_INIT(sbuff);
2139
2140
0
  if (needle_len == SIZE_MAX) needle_len = strlen(needle);
2141
0
  if (!needle_len) return NULL;
2142
2143
  /*
2144
   *  Needle bigger than haystack
2145
   */
2146
0
  if (len < needle_len) return NULL;
2147
2148
0
  while (total <= (len - needle_len)) {
2149
0
    char *p, *end;
2150
0
    char const *n_p;
2151
2152
0
    if (fr_sbuff_extend_lowat(NULL, &our_sbuff, needle_len) < needle_len) break;
2153
2154
0
    for (p = our_sbuff.p, n_p = needle, end = our_sbuff.p + needle_len;
2155
0
         (p < end) && (tolower((uint8_t) *p) == tolower((uint8_t) *n_p));
2156
0
         p++, n_p++);
2157
0
    if (p == end) {
2158
0
      (void)fr_sbuff_set(sbuff, our_sbuff.p);
2159
0
      return sbuff->p;
2160
0
    }
2161
2162
0
    total += fr_sbuff_advance(&our_sbuff, 1);
2163
0
  }
2164
2165
0
  return NULL;
2166
0
}
2167
2168
/** Return true if the current char matches, and if it does, advance
2169
 *
2170
 * @param[in] sbuff to search for char in.
2171
 * @param[in] c   char to search for.
2172
 * @return
2173
 *  - true and advance if the next character matches.
2174
 *  - false and don't advance if the next character doesn't match.
2175
 */
2176
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
2177
350k
{
2178
350k
  CHECK_SBUFF_INIT(sbuff);
2179
2180
350k
  if (!fr_sbuff_extend(sbuff)) return false;
2181
2182
334k
  if (*sbuff->p != c) return false;
2183
2184
64.2k
  fr_sbuff_advance(sbuff, 1);
2185
2186
64.2k
  return true;
2187
334k
}
2188
2189
/** Return true and advance if the next char does not match
2190
 *
2191
 * @param[in] sbuff to search for char in.
2192
 * @param[in] c   char to search for.
2193
 * @return
2194
 *  - true and advance unless the character matches.
2195
 *  - false and don't advance if the next character matches.
2196
 */
2197
bool fr_sbuff_next_unless_char(fr_sbuff_t *sbuff, char c)
2198
0
{
2199
0
  CHECK_SBUFF_INIT(sbuff);
2200
2201
0
  if (!fr_sbuff_extend(sbuff)) return false;
2202
2203
0
  if (*sbuff->p == c) return false;
2204
2205
0
  fr_sbuff_advance(sbuff, 1);
2206
2207
0
  return true;
2208
0
}
2209
2210
/** Trim trailing characters from a string we're composing
2211
 *
2212
 * @param[in] sbuff   to trim trailing characters from.
2213
 * @param[in] to_trim   Charset to trim.
2214
 * @return how many chars we removed.
2215
 */
2216
size_t fr_sbuff_trim(fr_sbuff_t *sbuff, bool const to_trim[static SBUFF_CHAR_CLASS])
2217
0
{
2218
0
  char  *p = sbuff->p - 1;
2219
0
  ssize_t slen;
2220
2221
0
  while ((p >= sbuff->start) && to_trim[(uint8_t)*p]) p--;
2222
2223
0
  slen = fr_sbuff_set(sbuff, p + 1);
2224
0
  if (slen != 0) fr_sbuff_terminate(sbuff);
2225
2226
0
  return slen;
2227
0
}
2228
2229
/** Efficient terminal string search
2230
 *
2231
 * Caller should ensure that a buffer extension of needle_len bytes has been requested
2232
 * before calling this function.
2233
 *
2234
 * @param[in] in  Sbuff to search in.
2235
 * @param[in] tt  Token terminals in the encompassing grammar.
2236
 * @return
2237
 *      - true if found.
2238
 *  - false if not.
2239
 */
2240
bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt)
2241
42.8k
{
2242
42.8k
  uint8_t   idx[SBUFF_CHAR_CLASS];  /* Fast path index */
2243
42.8k
  size_t    needle_len = 1;
2244
2245
  /*
2246
   *  No terminal, check for EOF.
2247
   */
2248
42.8k
  if (!tt) {
2249
2.90k
    fr_sbuff_extend_status_t status = 0;
2250
2251
2.90k
    if ((fr_sbuff_extend_lowat(&status, in, 1) == 0) &&
2252
992
        (status & FR_SBUFF_FLAG_EXTEND_ERROR) == 0) {
2253
992
      return true;
2254
992
    }
2255
2256
1.91k
    return false;
2257
2.90k
  }
2258
2259
  /*
2260
   *  Initialise the fastpath index and
2261
   *  figure out the longest needle.
2262
   */
2263
39.9k
  fr_sbuff_terminal_idx_init(&needle_len, idx, tt);
2264
2265
39.9k
  fr_sbuff_extend_lowat(NULL, in, needle_len);
2266
2267
39.9k
  return fr_sbuff_terminal_search(in, in->p, idx, tt, needle_len);
2268
42.8k
}
2269
2270
/** Print a char in a friendly format
2271
 *
2272
 */
2273
static char const *sbuff_print_char(char c)
2274
0
{
2275
0
  static bool const unprintables[SBUFF_CHAR_CLASS] = {
2276
0
    SBUFF_CHAR_UNPRINTABLES_LOW,
2277
0
    SBUFF_CHAR_UNPRINTABLES_EXTENDED
2278
0
  };
2279
2280
0
  static _Thread_local char str[10][5];
2281
0
  static _Thread_local size_t i = 0;
2282
2283
0
  switch (c) {
2284
0
  case '\a':
2285
0
    return "\a";
2286
2287
0
  case '\b':
2288
0
    return "\b";
2289
2290
0
  case '\n':
2291
0
    return "\n";
2292
2293
0
  case '\r':
2294
0
    return "\r";
2295
2296
0
  case '\t':
2297
0
    return "\t";
2298
2299
0
  case '\f':
2300
0
    return "\f";
2301
2302
0
  case '\v':
2303
0
    return "\v";
2304
2305
0
  default:
2306
0
    if (i >= NUM_ELEMENTS(str)) i = 0;
2307
2308
0
    if (unprintables[(uint8_t)c]) {
2309
0
      snprintf(str[i], sizeof(str[i]), "\\x%02x", (uint8_t) c);
2310
0
      return str[i++];
2311
0
    }
2312
2313
0
    str[i][0] = c;
2314
0
    str[i][1] = '\0';
2315
0
    return str[i++];
2316
0
  }
2317
0
}
2318
2319
void fr_sbuff_unescape_debug(FILE *fp, fr_sbuff_unescape_rules_t const *escapes)
2320
0
{
2321
0
  int i;
2322
2323
0
  fprintf(fp, "Escape rules %s (%p)\n", escapes->name, escapes);
2324
0
  fprintf(fp, "chr     : %c\n", escapes->chr ? escapes->chr : ' ');
2325
0
  fprintf(fp, "do_hex  : %s\n", escapes->do_hex ? "yes" : "no");
2326
0
  fprintf(fp, "do_oct  : %s\n", escapes->do_oct ? "yes" : "no");
2327
2328
0
  fprintf(fp, "substitutions:\n");
2329
0
  for (i = 0; i < SBUFF_CHAR_CLASS; i++) {
2330
0
    if (escapes->subs[i]) FR_FAULT_LOG("\t%s -> %s\n",
2331
0
               sbuff_print_char((char)i),
2332
0
               sbuff_print_char((char)escapes->subs[i]));
2333
0
  }
2334
0
  fprintf(fp, "skips:\n");
2335
0
  for (i = 0; i < SBUFF_CHAR_CLASS; i++) {
2336
0
    if (escapes->skip[i]) fprintf(fp, "\t%s\n", sbuff_print_char((char)i));
2337
0
  }
2338
0
}
2339
2340
void fr_sbuff_terminal_debug(FILE *fp, fr_sbuff_term_t const *tt)
2341
0
{
2342
0
  size_t i;
2343
2344
0
  fprintf(fp, "Terminal count %zu\n", tt->len);
2345
2346
0
  for (i = 0; i < tt->len; i++) fprintf(fp, "\t\"%s\" (%zu)\n", tt->elem[i].str, tt->elem[i].len);
2347
0
}
2348
2349
void fr_sbuff_parse_rules_debug(FILE *fp, fr_sbuff_parse_rules_t const *p_rules)
2350
0
{
2351
0
  fprintf(fp, "Parse rules %p\n", p_rules);
2352
2353
0
  FR_FAULT_LOG("Escapes - ");
2354
0
  if (p_rules->escapes) {
2355
0
    fr_sbuff_unescape_debug(fp, p_rules->escapes);
2356
0
  } else {
2357
0
    fprintf(fp, "<none>\n");
2358
0
  }
2359
2360
0
  FR_FAULT_LOG("Terminals - ");
2361
0
  if (p_rules->terminals) {
2362
0
    fr_sbuff_terminal_debug(fp, p_rules->terminals);
2363
0
  } else {
2364
0
    fprintf(fp, "<none>\n");
2365
0
  }
2366
0
}
2367
2368
/** Concat an array of strings (not NULL terminated), with a string separator
2369
 *
2370
 * @param[out] out  Where to write the resulting string.
2371
 * @param[in] array of strings to concat.
2372
 * @param[in] sep to insert between elements.  May be NULL.
2373
 * @return
2374
 *      - >= 0 on success - length of the string created.
2375
 *  - <0 on failure.  How many bytes we would need.
2376
 */
2377
fr_slen_t fr_sbuff_array_concat(fr_sbuff_t *out, char const * const *array, char const *sep)
2378
0
{
2379
0
  fr_sbuff_t    our_out = FR_SBUFF(out);
2380
0
  size_t      len = talloc_array_length(array);
2381
0
  char const * const *  p;
2382
0
  char const * const *  end;
2383
0
  fr_sbuff_escape_rules_t e_rules = {
2384
0
          .name = __FUNCTION__,
2385
0
          .chr = '\\'
2386
0
        };
2387
2388
0
  if (sep) e_rules.subs[(uint8_t)*sep] = *sep;
2389
2390
0
  for (p = array, end = array + len;
2391
0
       (p < end);
2392
0
       p++) {
2393
0
    if (*p) FR_SBUFF_RETURN(fr_sbuff_in_escape, &our_out, *p, strlen(*p), &e_rules);
2394
2395
0
    if (sep && ((p + 1) < end)) {
2396
0
      FR_SBUFF_RETURN(fr_sbuff_in_strcpy, &our_out, sep);
2397
0
    }
2398
0
  }
2399
2400
0
  FR_SBUFF_SET_RETURN(out, &our_out);
2401
0
}