/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 | } |