Coverage Report

Created: 2026-09-28 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/print.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
/** Functions to produce and parse the FreeRADIUS presentation format
18
 *
19
 * @file src/lib/util/print.c
20
 *
21
 * @copyright 2000,2006 The FreeRADIUS server project
22
 */
23
RCSID("$Id: 34fab93ebd8cf61b6b84dbf21891e9f728c5af0c $")
24
25
#include <freeradius-devel/util/debug.h>
26
#include <freeradius-devel/util/base16.h>
27
#include <freeradius-devel/util/pair.h>
28
29
30
/** Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8
31
 *
32
 * @param[in] str input string.
33
 * @param[in] inlen length of input string.  May be -1 if str
34
 *      is \0 terminated.
35
 * @return
36
 *  - 0 if the character is invalid.
37
 *  - >0 the number of bytes the character consists of.
38
 */
39
inline size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
40
34.2M
{
41
34.2M
  if (inlen == 0) return 0;
42
43
34.2M
  if (inlen < 0) {
44
0
    if (*str < 0x20) return 0; /* end of string, or control characters. */
45
46
    /*
47
     *  The trailing zero can occur at any point in
48
     *  the next 4 characters.
49
     */
50
0
    for (inlen = 1; inlen <= 4; inlen++) {
51
0
      if (!str[inlen]) break;
52
0
    }
53
0
  }
54
55
34.2M
  if (*str <= 0x7f) return 1;  /* 1 */
56
57
8.67M
  if (*str <= 0xc1) return 0;
58
59
4.84M
  if (inlen < 2) return 0;
60
61
4.84M
  if ((str[0] >= 0xc2) &&   /* 2 */
62
4.84M
      (str[0] <= 0xdf) &&
63
686k
      (str[1] >= 0x80) &&
64
347k
      (str[1] <= 0xbf)) {
65
154k
    return 2;
66
154k
  }
67
68
4.69M
  if (inlen < 3) return 0;
69
70
4.69M
  if ((str[0] == 0xe0) &&   /* 3 */
71
35.1k
      (str[1] >= 0xa0) &&
72
20.9k
      (str[1] <= 0xbf) &&
73
11.7k
      (str[2] >= 0x80) &&
74
9.78k
      (str[2] <= 0xbf)) {
75
1.92k
    return 3;
76
1.92k
  }
77
78
4.68M
  if ((str[0] >= 0xe1) &&   /* 4a */
79
4.12M
      (str[0] <= 0xec) &&
80
348k
      (str[1] >= 0x80) &&
81
121k
      (str[1] <= 0xbf) &&
82
59.3k
      (str[2] >= 0x80) &&
83
30.1k
      (str[2] <= 0xbf)) {
84
15.6k
    return 3;
85
15.6k
  }
86
87
4.67M
  if ((str[0] >= 0xee) &&   /* 4b */
88
3.74M
      (str[0] <= 0xef) &&
89
405k
      (str[1] >= 0x80) &&
90
381k
      (str[1] <= 0xbf) &&
91
11.8k
      (str[2] >= 0x80) &&
92
6.65k
      (str[2] <= 0xbf)) {
93
3.28k
    return 3;
94
3.28k
  }
95
96
4.67M
  if ((str[0] == 0xed) &&   /* 5 */
97
32.8k
      (str[1] >= 0x80) &&
98
17.7k
      (str[1] <= 0x9f) &&
99
5.34k
      (str[2] >= 0x80) &&
100
3.20k
      (str[2] <= 0xbf)) {
101
1.61k
    return 3;
102
1.61k
  }
103
104
4.66M
  if (inlen < 4) return 0;
105
106
4.66M
  if ((str[0] == 0xf0) &&   /* 6 */
107
29.4k
      (str[1] >= 0x90) &&
108
20.3k
      (str[1] <= 0xbf) &&
109
8.48k
      (str[2] >= 0x80) &&
110
6.41k
      (str[2] <= 0xbf) &&
111
4.66k
      (str[3] >= 0x80) &&
112
3.51k
      (str[3] <= 0xbf)) {
113
1.80k
    return 4;
114
1.80k
  }
115
116
4.66M
  if ((str[0] >= 0xf1) &&   /* 6 */
117
3.30M
      (str[0] <= 0xf3) &&
118
81.2k
      (str[1] >= 0x80) &&
119
47.7k
      (str[1] <= 0xbf) &&
120
22.5k
      (str[2] >= 0x80) &&
121
14.1k
      (str[2] <= 0xbf) &&
122
8.93k
      (str[3] >= 0x80) &&
123
5.32k
      (str[3] <= 0xbf)) {
124
3.02k
    return 4;
125
3.02k
  }
126
127
128
4.66M
  if ((str[0] == 0xf4) &&   /* 7 */
129
20.2k
      (str[1] >= 0x80) &&
130
14.6k
      (str[1] <= 0x8f) &&
131
7.50k
      (str[2] >= 0x80) &&
132
5.49k
      (str[2] <= 0xbf) &&
133
3.45k
      (str[3] >= 0x80) &&
134
2.52k
      (str[3] <= 0xbf)) {
135
1.22k
    return 4;
136
1.22k
  }
137
138
  /*
139
   *  Invalid UTF-8 Character
140
   */
141
4.66M
  return 0;
142
4.66M
}
143
144
/** Validate a complete UTF8 string
145
 *
146
 * @param[in] str input string.
147
 * @param[in] inlen length of input string.  May be -1 if str
148
 *      is \0 terminated.
149
 * @return The number of bytes validated.  If ret == inlen the entire
150
 *     string is valid.  Else ret gives the negative offset at
151
 *     which the first invalid byte sequence was found.
152
 */
153
fr_slen_t fr_utf8_str(uint8_t const *str, ssize_t inlen)
154
0
{
155
0
  uint8_t const *p, *end;
156
0
  size_t len;
157
158
0
  len = inlen < 0 ? strlen((char const *)str) : (size_t) inlen;
159
160
0
  p = str;
161
0
  end = p + len;
162
163
0
  do {
164
0
    size_t clen;
165
166
0
    clen = fr_utf8_char(p, end - p);
167
0
    if (clen == 0) return p - end;
168
0
    p += clen;
169
0
  } while (p < end);
170
171
0
  return inlen;
172
0
}
173
174
/** Return a pointer to the first UTF8 char in a string.
175
 *
176
 * @param[out] out_chr_len  Where to write the length of the multibyte char passed in chr (may be NULL).
177
 * @param[in] str   Haystack.
178
 * @param[in] inlen   Length of string (in bytes).  Pass -1 to determine the length of the string.
179
 * @param[in] chr   Multibyte needle.
180
 * @return
181
 *  - Position of chr in str.
182
 *  - NULL if not found.
183
 */
184
char const *fr_utf8_strchr(int *out_chr_len, char const *str, ssize_t inlen, char const *chr)
185
0
{
186
0
  char const  *p = str, *end;
187
0
  int   needle_len;
188
189
0
  if (inlen < 0) inlen = strlen(str);
190
191
0
  end = str + inlen;
192
193
  /*
194
   *  Figure out how big the multibyte sequence
195
   *  we're looking for is.
196
   */
197
0
  needle_len = fr_utf8_char((uint8_t const *)chr, -1);
198
0
  if (needle_len == 0) needle_len = 1; /* Invalid UTF8 sequence - ignore - needle is one byte */
199
0
  if (out_chr_len) *out_chr_len = needle_len;
200
201
  /*
202
   *  Loop over the input sequence, advancing
203
   *      UTF8 sequence by utf8 seqnce.
204
   */
205
0
  while (p < end) {
206
0
    int schr_len;
207
208
0
    schr_len = fr_utf8_char((uint8_t const *)p, end - p);
209
0
    if (schr_len == 0) schr_len = 1; /* Invalid UTF8 sequence - ignore - advance by 1 */
210
0
    if (schr_len != needle_len) goto next;
211
212
    /*
213
     *  See if this matches out multibyte needle
214
     */
215
0
    if (memcmp(p, chr, schr_len) == 0) return p;
216
0
  next:
217
0
    p += schr_len;
218
0
  }
219
220
0
  return NULL;
221
0
}
222
223
/** Escape any non printable or non-UTF8 characters in the input string
224
 *
225
 * @note Return value should be checked with is_truncated
226
 * @note Will always \0 terminate unless outlen == 0.
227
 *
228
 * @param[out] out  where to write the escaped string.
229
 * @param[out] outlen the length of the buffer pointed to by out.
230
 * @param[in] in  string to escape.
231
 * @param[in] inlen length of string to escape (lets us deal with embedded NULs)
232
 * @param[in] quote the quotation character
233
 * @return
234
 *  - The number of bytes written to the out buffer.
235
 *  - A number >= outlen if truncation has occurred.
236
 */
237
size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
238
0
{
239
0
  uint8_t const *p = (uint8_t const *) in;
240
0
  size_t    utf8;
241
0
  size_t    used;
242
0
  size_t    freespace;
243
244
  /* No input, so no output... */
245
0
  if (!in) {
246
0
    if (out && outlen) *out = '\0';
247
0
    return 0;
248
0
  }
249
250
  /* Figure out the length of the input string */
251
0
  if (inlen < 0) inlen = strlen(in);
252
253
  /*
254
   *  No quotation character, just use memcpy, ensuring we
255
   *  don't overflow the output buffer.
256
   */
257
0
  if (!quote) {
258
0
    if (!out) return inlen;
259
260
0
    if ((size_t)inlen >= outlen) {
261
0
      memcpy(out, in, outlen - 1);
262
0
      out[outlen - 1] = '\0';
263
0
    } else {
264
0
      memcpy(out, in, inlen);
265
0
      out[inlen] = '\0';
266
0
    }
267
268
0
    return inlen;
269
0
  }
270
271
  /*
272
   *  Check the output buffer and length.  Zero both of them
273
   *  out if either are zero.
274
   */
275
0
  freespace = outlen;
276
0
  if (freespace == 0) out = NULL;
277
0
  if (!out) freespace = 0;
278
279
0
  used = 0;
280
281
0
  while (inlen > 0) {
282
0
    int sp = 0;
283
284
    /*
285
     *  Always escape the quotation character.
286
     */
287
0
    if (*p == quote) {
288
0
      sp = quote;
289
0
      goto do_escape;
290
0
    }
291
292
    /*
293
     *  Escape the backslash ONLY for single quoted strings.
294
     */
295
0
    if (quote == '\'') {
296
0
      if (*p == '\\') {
297
0
        sp = '\\';
298
0
      }
299
0
      goto do_escape;
300
0
    }
301
302
    /*
303
     *  Try to convert 0x0a --> \r, etc.
304
     *  Backslashes get handled specially.
305
     */
306
0
    switch (*p) {
307
0
    case '\r':
308
0
      sp = 'r';
309
0
      break;
310
311
0
    case '\n':
312
0
      sp = 'n';
313
0
      break;
314
315
0
    case '\t':
316
0
      sp = 't';
317
0
      break;
318
319
0
    case '\\':
320
0
      sp = '\\';
321
0
      break;
322
323
0
    default:
324
0
      sp = '\0';
325
0
      break;
326
0
    } /* escape the character at *p */
327
328
0
  do_escape:
329
0
    if (sp) {
330
0
      if ((freespace > 0) && (freespace <= 2)) {
331
0
        if (out) out[used] = '\0';
332
0
        out = NULL;
333
0
        freespace = 0;
334
335
0
      } else if (freespace > 2) { /* room for char AND trailing zero */
336
0
        if (out) {
337
0
          out[used] = '\\';
338
0
          out[used + 1] = sp;
339
0
        }
340
0
        freespace -= 2;
341
0
      }
342
343
0
      used += 2;
344
0
      p++;
345
0
      inlen--;
346
0
      continue;
347
0
    }
348
349
    /*
350
     *  All strings are UTF-8 clean.
351
     */
352
0
    utf8 = fr_utf8_char(p, inlen);
353
354
    /*
355
     *  If we have an invalid UTF-8 character, it gets
356
     *  copied over as a 1-byte character for single
357
     *  quoted strings.  Which means that the output
358
     *  isn't strictly UTF-8, but oh well...
359
     *
360
     *  For double quoted strints, the invalid
361
     *  characters get escaped as octal encodings.
362
     */
363
0
    if (utf8 == 0) {
364
0
      if (quote == '\'') {
365
0
        utf8 = 1;
366
367
0
      } else {
368
0
        if ((freespace > 0) && (freespace <= 4)) {
369
0
          if (out) out[used] = '\0';
370
0
          out = NULL;
371
0
          freespace = 0;
372
373
0
        } else if (freespace > 4) { /* room for char AND trailing zero */
374
0
          if (out) snprintf(out + used, freespace, "\\%03o", *p);
375
0
          freespace -= 4;
376
0
        }
377
378
0
        used += 4;
379
0
        p++;
380
0
        inlen--;
381
0
        continue;
382
0
      }
383
0
    }
384
385
0
    if ((freespace > 0) && (freespace <= utf8)) {
386
0
      if (out) out[used] = '\0';
387
0
      out = NULL;
388
0
      freespace = 0;
389
390
0
    } else if (freespace > utf8) { /* room for char AND trailing zero */
391
0
      if (out) memcpy(out + used, p, utf8);
392
0
      freespace -= utf8;
393
0
    }
394
395
0
    used += utf8;
396
0
    p += utf8;
397
0
    inlen -= utf8;
398
0
  }
399
400
  /*
401
   *  Ensure that the output buffer is always zero terminated.
402
   */
403
0
  if (out && freespace) out[used] = '\0';
404
405
0
  return used;
406
0
}
407
408
/** Find the length of the buffer required to fully escape a string with fr_prints
409
 *
410
 * Were assuming here that's it's cheaper to figure out the length and do one
411
 * alloc than repeatedly expand the buffer when we find extra chars which need
412
 * to be added.
413
 *
414
 * @param in string to calculate the escaped length for.
415
 * @param inlen length of the input string, if < 0 strlen will be used to check the length.
416
 * @param[in] quote the quotation character.
417
 * @return the size of buffer required to hold the escaped string including the NUL byte.
418
 */
419
size_t fr_snprint_len(char const *in, ssize_t inlen, char quote)
420
0
{
421
0
  return fr_snprint(NULL, 0, in, inlen, quote) + 1;
422
0
}
423
424
/** Escape string that may contain binary data, and write it to a new buffer
425
 *
426
 * This is useful in situations where we expect printable strings as input,
427
 * but under some conditions may get binary data. A good example is libldap
428
 * and the arrays of struct berval ldap_get_values_len returns.
429
 *
430
 * @param[in] ctx To allocate new buffer in.
431
 * @param[in] in  String to escape.
432
 * @param[in] inlen Length of string. Should be >= 0 if the data may contain
433
 *      embedded \0s. Must be >= 0 if data may not be \0 terminated.
434
 *      If < 0 inlen will be calculated using strlen.
435
 * @param[in] quote the quotation character.
436
 * @return new    buffer holding the escaped string.
437
 */
438
char *fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
439
{
440
  size_t len, ret;
441
  char *out;
442
443
  len = fr_snprint_len(in, inlen, quote);
444
445
  out = talloc_array(ctx, char, len);
446
  ret = fr_snprint(out, len, in, inlen, quote);
447
  /*
448
   *  This is a fatal error, but fr_cond_assert is the strongest
449
   *  assert we're allowed to use in library functions.
450
   */
451
  if (!fr_cond_assert(ret == (len - 1))) {
452
    talloc_free(out);
453
    return NULL;
454
  }
455
456
  return out;
457
}
458
459
DIAG_OFF(format-nonliteral)
460
/** Special version of vasprintf which implements custom format specifiers
461
 *
462
 * @todo Do something sensible with 'n$', though it's probably not actually used
463
 *  anywhere in our code base.
464
 *
465
 * - %pV prints a value box as a string.
466
 * - %pM prints a list of value boxes, concatenating them.
467
 * - %pH prints a value box as a hex string.
468
 * - %pP prints a fr_pair_t.
469
 *
470
 * This breaks strict compatibility with printf but allows us to continue using
471
 * the static format string and argument type validation.
472
 *
473
 * This same idea is used in Linux for the printk function.
474
 *
475
 * @param[in] ctx to allocate buffer in.
476
 * @param[in] fmt string.
477
 * @param[in] ap  variadic argument list.
478
 * @param[in] suppress_secrets as described
479
 * @return
480
 *  - The result of string interpolation.
481
 *  - NULL if OOM.
482
 */
483
static char *fr_vasprintf_internal(TALLOC_CTX *ctx, char const *fmt, va_list ap, bool suppress_secrets)
484
24.3M
{
485
24.3M
  char const  *p = fmt, *end = p + strlen(fmt), *fmt_p = p, *fmt_q = p;
486
24.3M
  char    *out = NULL, *out_tmp;
487
24.3M
  va_list   ap_p, ap_q;
488
24.3M
  char    *subst;
489
490
24.3M
  out = talloc_strdup(ctx, "");
491
24.3M
  va_copy(ap_p, ap);
492
24.3M
  va_copy(ap_q, ap_p);
493
494
59.0M
  do {
495
59.0M
    char const  *q;
496
59.0M
    char    len[2] = { '\0', '\0' };
497
498
59.0M
    subst = NULL;
499
500
59.0M
    if ((*p != '%') || (*++p == '%')) {
501
13.9M
      fmt_q = p + 1;
502
13.9M
      continue; /* literal char */
503
13.9M
    }
504
505
    /*
506
     *  Check for parameter field
507
     */
508
45.0M
    for (q = p; isdigit((uint8_t) *q); q++);
509
45.0M
    if ((q != p) && (*q == '$')) {
510
0
      p = q + 1;
511
0
    }
512
513
    /*
514
     *  Check for flags
515
     */
516
65.3M
    do {
517
65.3M
      switch (*p) {
518
0
      case '-':
519
0
        continue;
520
521
0
      case '+':
522
0
        continue;
523
524
0
      case ' ':
525
0
        continue;
526
527
20.3M
      case '0':
528
20.3M
        continue;
529
530
25
      case '#':
531
25
        continue;
532
533
45.0M
      default:
534
45.0M
        goto done_flags;
535
65.3M
      }
536
65.3M
    } while (++p < end);
537
45.0M
  done_flags:
538
539
    /*
540
     *  Check for width field.  First for strings, and
541
     *  then for other parameters.
542
     */
543
45.0M
    if ((*p == '.') && (*(p + 1) == '*') && (*(p + 2) == 's')) {
544
165
      (void) va_arg(ap_q, int);
545
165
      p += 2;
546
45.0M
    } else if (*p == '*') {
547
0
      (void) va_arg(ap_q, int);
548
0
      p++;
549
45.0M
    } else {
550
45.0M
      for (q = p; isdigit((uint8_t) *q); q++);
551
45.0M
      p = q;
552
45.0M
    }
553
554
    /*
555
     *  Check for precision field
556
     */
557
45.0M
    if (*p == '.') {
558
0
      char *r;
559
560
0
      p++;
561
0
      (void) strtoul(p, &r, 10);
562
0
      p = r;
563
0
    }
564
565
    /*
566
     *  Length modifiers
567
     */
568
45.0M
    switch (*p) {
569
0
    case 'h':
570
1.21k
    case 'l':
571
1.21k
      len[0] = *p++;
572
1.21k
      if ((*p == 'h') || (*p == 'l')) len[1] = *p++;
573
1.21k
      break;
574
575
0
    case 'L':
576
96.3k
    case 'z':
577
96.3k
    case 'j':
578
96.3k
    case 't':
579
96.3k
      len[0] = *p++;
580
96.3k
      break;
581
45.0M
    }
582
583
    /*
584
     *  Types
585
     */
586
45.0M
    switch (*p) {
587
760
    case 'i':               /* int */
588
37.4k
    case 'd':               /* int */
589
3.88M
    case 'u':               /* unsigned int */
590
3.92M
    case 'x':               /* unsigned int */
591
3.92M
    case 'X':               /* unsigned int */
592
24.2M
    case 'o':               /* unsigned int */
593
24.2M
      switch (len[0]) {
594
0
      case 'h':
595
0
        if (len[1] == 'h') {         /* char (promoted to int) */
596
0
          (void) va_arg(ap_q, int);
597
0
        } else {
598
0
          (void) va_arg(ap_q, int);      /* short (promoted to int) */
599
0
        }
600
0
        break;
601
602
1.21k
      case 'l':
603
1.21k
        if ((*p == 'i') || (*p == 'd')) {
604
314
          if (len[1] == 'l') {
605
0
            (void) va_arg(ap_q, long long);    /* long long */
606
314
          } else {
607
314
            (void) va_arg(ap_q, long);   /* long */
608
314
          }
609
898
        } else {
610
898
          if (len[1] == 'l') {
611
0
            (void) va_arg(ap_q, unsigned long long); /* unsigned long long */
612
898
          } else {
613
898
            (void) va_arg(ap_q, unsigned long);  /* unsigned long */
614
898
          }
615
898
        }
616
1.21k
        break;
617
618
96.3k
      case 'z':
619
96.3k
        (void) va_arg(ap_q, size_t);       /* size_t */
620
96.3k
        break;
621
622
0
      case 'j':
623
0
        (void) va_arg(ap_q, intmax_t);       /* intmax_t */
624
0
        break;
625
626
0
      case 't':
627
0
        (void) va_arg(ap_q, ptrdiff_t);        /* ptrdiff_t */
628
0
        break;
629
630
24.1M
      case '\0':  /* no length modifier */
631
24.1M
        if ((*p == 'i') || (*p == 'd')) {
632
34.5k
          (void) va_arg(ap_q, int);      /* int */
633
24.0M
        } else {
634
24.0M
          (void) va_arg(ap_q, unsigned int);   /* unsigned int */
635
24.0M
        }
636
24.2M
      }
637
24.2M
      break;
638
639
24.2M
    case 'f':               /* double */
640
0
    case 'F':               /* double */
641
0
    case 'e':               /* double */
642
0
    case 'E':               /* double */
643
107
    case 'g':               /* double */
644
107
    case 'G':               /* double */
645
107
    case 'a':               /* double */
646
107
    case 'A':               /* double */
647
107
      switch (len[0]) {
648
0
      case 'L':
649
0
        (void) va_arg(ap_q, long double);      /* long double */
650
0
        break;
651
652
0
      case 'l': /* does nothing */
653
107
      default:  /* no length modifier */
654
107
        (void) va_arg(ap_q, double);       /* double */
655
107
      }
656
107
      break;
657
658
547k
    case 's':
659
547k
      (void) va_arg(ap_q, char *);         /* char * */
660
547k
      break;
661
662
20.2M
    case 'c':
663
20.2M
      (void) va_arg(ap_q, int);          /* char (promoted to int) */
664
20.2M
      break;
665
666
7.80k
    case 'p':
667
      /*
668
       *  subst types
669
       */
670
7.80k
      switch (*(p + 1)) {
671
0
      case 'R':
672
        /*
673
         *  If the caller explicitly asks to suppress secrets via '%pR', then we
674
         *  do that.
675
         */
676
0
        suppress_secrets = true;
677
0
        FALL_THROUGH;
678
679
7.80k
      case 'V':
680
7.80k
      {
681
7.80k
        fr_value_box_t const *in = va_arg(ap_q, fr_value_box_t const *);
682
683
        /*
684
         *  Allocations that are not part of the output
685
         *  string need to occur in the NULL ctx so we don't fragment
686
         *  any pool associated with it.
687
         */
688
7.80k
        if (unlikely(in && fr_value_box_is_secret(in) && suppress_secrets)) {
689
0
          subst = talloc_strdup(NULL, "<<< secret >>>");
690
691
7.80k
        } else if (in) {
692
          /*
693
           *  Value boxes get escaped as double-quoted strings.
694
           */
695
7.80k
          fr_value_box_aprint(NULL, &subst, in, &fr_value_escape_double);
696
697
7.80k
        } else {
698
0
          subst = talloc_strdup(NULL, "(null)");
699
0
        }
700
701
7.80k
      do_splice:
702
7.80k
        if (!subst) goto oom;
703
704
7.80k
        p++;
705
706
        /*
707
         *  Pass part of a format string to printf
708
         */
709
7.80k
        if (fmt_q != fmt_p) {
710
7.80k
          char *sub_fmt;
711
712
7.80k
          sub_fmt = talloc_strndup(NULL, fmt_p, fmt_q - fmt_p);
713
7.80k
          out_tmp = talloc_vasprintf_append_buffer(out, sub_fmt, ap_p);
714
7.80k
          talloc_free(sub_fmt);
715
7.80k
          if (!out_tmp) {
716
0
          oom:
717
0
            fr_strerror_const("Out of memory");
718
0
            talloc_free(out);
719
0
            TALLOC_FREE(subst);
720
0
            va_end(ap_p);
721
0
            va_end(ap_q);
722
0
            return NULL;
723
0
          }
724
7.80k
          out = out_tmp;
725
726
7.80k
          out_tmp = talloc_strdup_append_buffer(out, subst);
727
7.80k
          if (!out_tmp) goto oom;
728
7.80k
          TALLOC_FREE(subst);
729
7.80k
          out = out_tmp;
730
731
7.80k
          va_end(ap_p);   /* one time use only */
732
7.80k
        } else {
733
0
          out_tmp = talloc_strdup_append_buffer(out, subst);
734
0
          if (!out_tmp) goto oom;
735
0
          TALLOC_FREE(subst);
736
0
          out = out_tmp;
737
0
        }
738
739
7.80k
        va_copy(ap_p, ap_q); /* already advanced to the next argument */
740
741
7.80k
        fmt_p = p + 1;
742
7.80k
      }
743
0
        break;
744
745
0
      case 'H':
746
0
      {
747
0
        fr_value_box_t const *in = va_arg(ap_q, fr_value_box_t const *);
748
749
0
        if (!in) {
750
0
          subst = talloc_strdup(NULL, "(null)");
751
0
          goto do_splice;
752
0
        }
753
754
0
        switch (in->type) {
755
0
        case FR_TYPE_OCTETS:
756
0
          if (in->vb_octets) {
757
0
            fr_base16_aencode(NULL, &subst, &FR_DBUFF_TMP(in->vb_octets, in->vb_length));
758
0
          } else {
759
0
            subst = talloc_strdup(NULL, "");
760
0
          }
761
0
          break;
762
763
0
        case FR_TYPE_STRING:
764
0
          fr_base16_aencode(NULL, &subst, &FR_DBUFF_TMP((uint8_t const *)in->vb_strvalue, in->vb_length));
765
0
          break;
766
767
0
        default:
768
0
        {
769
0
          fr_value_box_t dst;
770
771
          /*
772
           *  Convert the boxed value into a octets buffer
773
           */
774
0
          if (fr_value_box_cast(NULL, &dst, FR_TYPE_OCTETS, NULL, in) < 0) {
775
0
            subst = talloc_strdup(NULL, fr_strerror()); /* splice in the error */
776
0
            if (!subst) goto oom;
777
0
          }
778
779
0
          fr_base16_aencode(NULL, &subst, &FR_DBUFF_TMP((uint8_t const *)dst.vb_octets, dst.vb_length));
780
0
          fr_value_box_clear(&dst);
781
0
          break;
782
0
        }
783
0
        }
784
0
      }
785
0
        goto do_splice;
786
787
0
      case 'M':
788
0
      {
789
0
        fr_value_box_list_t const *in = va_arg(ap_q, fr_value_box_list_t const *);
790
791
0
        if (!in) {
792
0
          subst = talloc_strdup(NULL, "(null)");
793
0
          goto do_splice;
794
0
        }
795
796
0
        if (suppress_secrets) {
797
0
          subst = fr_value_box_list_aprint_secure(NULL, in, NULL, &fr_value_escape_double);
798
0
        } else {
799
0
          subst = fr_value_box_list_aprint(NULL, in, NULL, &fr_value_escape_double);
800
0
        }
801
0
      }
802
0
        goto do_splice;
803
804
0
      case 'P':
805
0
      {
806
0
        fr_pair_t const *in = va_arg(ap_q, fr_pair_t const *);
807
808
0
        if (!in) {
809
0
          subst = talloc_strdup(NULL, "(null)");
810
0
          goto do_splice;
811
0
        }
812
813
0
        PAIR_VERIFY(in);
814
815
0
        if (unlikely(in && suppress_secrets)) {
816
0
          fr_pair_aprint_secure(NULL, &subst, NULL, in);
817
0
        } else {
818
0
          fr_pair_aprint(NULL, &subst, NULL, in);
819
0
        }
820
0
      }
821
0
        goto do_splice;
822
823
0
      default:
824
0
        (void) va_arg(ap_q, void *);       /* void * */
825
7.80k
      }
826
7.80k
      break;
827
828
7.80k
    case 'n':
829
0
      (void) va_arg(ap_q, int *);          /* int * */
830
0
      break;
831
832
0
    default:
833
0
      break;
834
45.0M
    }
835
45.0M
    fmt_q = p + 1;
836
59.0M
  } while (++p < end);
837
838
  /*
839
   *  Print out the rest of the format string.
840
   */
841
24.3M
  if (*fmt_p) {
842
24.3M
    out_tmp = talloc_vasprintf_append_buffer(out, fmt_p, ap_p);
843
24.3M
    if (!out_tmp) goto oom;
844
24.3M
    out = out_tmp;
845
24.3M
  }
846
847
24.3M
  va_end(ap_p);
848
24.3M
  va_end(ap_q);
849
850
  /*
851
   *  One of the above talloc calls sets the type to
852
   *  be the string.  We correct this here so we
853
   *  don't trigger talloc_aborts later...
854
   */
855
24.3M
  talloc_set_type(out, char);
856
857
24.3M
  return out;
858
24.3M
}
859
860
char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
861
24.3M
{
862
24.3M
  return fr_vasprintf_internal(ctx, fmt, ap, false);
863
24.3M
}
864
865
char *fr_vasprintf_secure(TALLOC_CTX *ctx, char const *fmt, va_list ap)
866
0
{
867
0
  return fr_vasprintf_internal(ctx, fmt, ap, true);
868
0
}
869
870
871
DIAG_ON(format-nonliteral)
872
873
/** Special version of asprintf which implements custom format specifiers
874
 *
875
 * @copybrief fr_vasprintf
876
 *
877
 * @param[in] ctx to allocate buffer in.
878
 * @param[in] fmt string.
879
 * @param[in] ... variadic argument list.
880
 * @return
881
 *  - The result of string interpolation.
882
 */
883
char *fr_asprintf(TALLOC_CTX *ctx, char const *fmt, ...)
884
2
{
885
2
  va_list ap;
886
2
  char *ret;
887
888
2
  va_start(ap, fmt);
889
2
  ret = fr_vasprintf(ctx, fmt, ap);
890
2
  va_end(ap);
891
892
2
  return ret;
893
2
}
894
895
/** Special version of fprintf which implements custom format specifiers
896
 *
897
 * @copybrief fr_vasprintf
898
 *
899
 * @param[in] fp  to write the result of fmt string.
900
 * @param[in] fmt string.
901
 * @param[in] ... variadic argument list.
902
 * @return
903
 *   - On success, the number of bytes written is returned (zero indicates nothing was written).
904
 *   - On error, -1 is returned, and errno is set appropriately
905
 */
906
ssize_t fr_fprintf(FILE *fp, char const *fmt, ...)
907
0
{
908
0
  va_list ap;
909
0
  char *buf;
910
0
  int ret;
911
912
0
  if (!fp) {
913
0
    fr_strerror_const("Invalid 'fp'");
914
0
    return -1;
915
0
  }
916
917
0
  va_start(ap, fmt);
918
0
  buf = fr_vasprintf(NULL, fmt, ap);
919
0
  va_end(ap);
920
921
0
  ret = fputs(buf, fp);
922
923
0
  TALLOC_FREE(buf);
924
925
0
  return ret;
926
0
}