Merge tag 'release_3_0_12' into branch moonshot-fr-3.0.12-upgrade.
[freeradius.git] / src / lib / print.c
1 /*
2  * print.c      Routines to print stuff.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  */
22
23 RCSID("$Id$")
24
25 #include        <freeradius-devel/libradius.h>
26
27 #include        <ctype.h>
28
29 /** Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8
30  *
31  * @param str input string.
32  * @param inlen length of input string.  May be -1 if str is \0 terminated.
33  */
34 int fr_utf8_char(uint8_t const *str, ssize_t inlen)
35 {
36         if (inlen == 0) return 0;
37
38         if (inlen < 0) inlen = 4;       /* longest char */
39
40         if (*str < 0x20) return 0;
41
42         if (*str <= 0x7e) return 1;     /* 1 */
43
44         if (*str <= 0xc1) return 0;
45
46         if (inlen < 2) return 0;
47
48         if ((str[0] >= 0xc2) &&         /* 2 */
49             (str[0] <= 0xdf) &&
50             (str[1] >= 0x80) &&
51             (str[1] <= 0xbf)) {
52                 return 2;
53         }
54
55         if (inlen < 3) return 0;
56
57         if ((str[0] == 0xe0) &&         /* 3 */
58             (str[1] >= 0xa0) &&
59             (str[1] <= 0xbf) &&
60             (str[2] >= 0x80) &&
61             (str[2] <= 0xbf)) {
62                 return 3;
63         }
64
65         if ((str[0] >= 0xe1) &&         /* 4a */
66             (str[0] <= 0xec) &&
67             (str[1] >= 0x80) &&
68             (str[1] <= 0xbf) &&
69             (str[2] >= 0x80) &&
70             (str[2] <= 0xbf)) {
71                 return 3;
72         }
73
74         if ((str[0] >= 0xee) &&         /* 4b */
75             (str[0] <= 0xef) &&
76             (str[1] >= 0x80) &&
77             (str[1] <= 0xbf) &&
78             (str[2] >= 0x80) &&
79             (str[2] <= 0xbf)) {
80                 return 3;
81         }
82
83         if ((str[0] == 0xed) &&         /* 5 */
84             (str[1] >= 0x80) &&
85             (str[1] <= 0x9f) &&
86             (str[2] >= 0x80) &&
87             (str[2] <= 0xbf)) {
88                 return 3;
89         }
90
91         if (inlen < 4) return 0;
92
93         if ((str[0] == 0xf0) &&         /* 6 */
94             (str[1] >= 0x90) &&
95             (str[1] <= 0xbf) &&
96             (str[2] >= 0x80) &&
97             (str[2] <= 0xbf) &&
98             (str[3] >= 0x80) &&
99             (str[3] <= 0xbf)) {
100                 return 4;
101         }
102
103         if ((str[0] >= 0xf1) &&         /* 6 */
104             (str[1] <= 0xf3) &&
105             (str[1] >= 0x80) &&
106             (str[1] <= 0xbf) &&
107             (str[2] >= 0x80) &&
108             (str[2] <= 0xbf) &&
109             (str[3] >= 0x80) &&
110             (str[3] <= 0xbf)) {
111                 return 4;
112         }
113
114
115         if ((str[0] == 0xf4) &&         /* 7 */
116             (str[1] >= 0x80) &&
117             (str[1] <= 0x8f) &&
118             (str[2] >= 0x80) &&
119             (str[2] <= 0xbf) &&
120             (str[3] >= 0x80) &&
121             (str[3] <= 0xbf)) {
122                 return 4;
123         }
124
125         /*
126          *      Invalid UTF-8 Character
127          */
128         return 0;
129 }
130
131 /** Return a pointer to the first UTF8 char in a string.
132  *
133  * @param[out] chr_len Where to write the length of the multibyte char passed in chr (may be NULL).
134  * @param[in] str Haystack.
135  * @param[in] chr Multibyte needle.
136  * @return The position of chr in str or NULL if not found.
137  */
138 char const *fr_utf8_strchr(int *chr_len, char const *str, char const *chr)
139 {
140         int cchr;
141
142         cchr = fr_utf8_char((uint8_t const *)chr, -1);
143         if (cchr == 0) cchr = 1;
144         if (chr_len) *chr_len = cchr;
145
146         while (*str) {
147                 int schr;
148
149                 schr = fr_utf8_char((uint8_t const *) str, -1);
150                 if (schr == 0) schr = 1;
151                 if (schr != cchr) goto next;
152
153                 if (memcmp(str, chr, schr) == 0) {
154                         return (char const *) str;
155                 }
156         next:
157                 str += schr;
158         }
159
160         return NULL;
161 }
162
163 /** Escape any non printable or non-UTF8 characters in the input string
164  *
165  * @note Return value should be checked with is_truncated
166  * @note Will always \0 terminate unless outlen == 0.
167  *
168  * @param[in] in string to escape.
169  * @param[in] inlen length of string to escape (lets us deal with embedded NULs)
170  * @param[out] out where to write the escaped string.
171  * @param[out] outlen the length of the buffer pointed to by out.
172  * @param[in] quote the quotation character
173  * @return the number of bytes it WOULD HAVE written to the buffer, not including the trailing NUL
174  */
175 size_t fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
176 {
177         uint8_t const   *p = (uint8_t const *) in;
178         size_t          utf8;
179         size_t          used;
180         size_t          freespace;
181
182         /* No input, so no output... */
183         if (!in) {
184                 if (out && outlen) *out = '\0';
185                 return 0;
186         }
187
188         /* Figure out the length of the input string */
189         if (inlen < 0) inlen = strlen(in);
190
191         /*
192          *      No quotation character, just use memcpy, ensuring we
193          *      don't overflow the output buffer.
194          */
195         if (!quote) {
196                 if (!out) return inlen;
197
198                 if ((size_t)inlen >= outlen) {
199                         memcpy(out, in, outlen - 1);
200                         out[outlen - 1] = '\0';
201                 } else {
202                         memcpy(out, in, inlen);
203                         out[inlen] = '\0';
204                 }
205
206                 return inlen;
207         }
208
209         /*
210          *      Check the output buffer and length.  Zero both of them
211          *      out if either are zero.
212          */
213         freespace = outlen;
214         if (freespace == 0) out = NULL;
215         if (!out) freespace = 0;
216
217         used = 0;
218
219         while (inlen > 0) {
220                 int sp = 0;
221
222                 /*
223                  *      Hack: never print trailing zero.
224                  *      Some clients send pings with an off-by-one
225                  *      length (confused with strings in C).
226                  */
227                 if ((inlen == 1) && (*p == '\0')) {
228                         inlen--;
229                         break;
230                 }
231
232                 /*
233                  *      Always escape the quotation character.
234                  */
235                 if (*p == quote) {
236                         sp = quote;
237                         goto do_escape;
238                 }
239
240                 /*
241                  *      Escape the backslash ONLY for single quoted strings.
242                  */
243                 if (quote == '\'') {
244                         if (*p == '\\') {
245                                 sp = '\\';
246                         }
247                         goto do_escape;
248                 }
249
250                 /*
251                  *      Try to convert 0x0a --> \r, etc.
252                  *      Backslashes get handled specially.
253                  */
254                 switch (*p) {
255                 case '\r':
256                         sp = 'r';
257                         break;
258
259                 case '\n':
260                         sp = 'n';
261                         break;
262
263                 case '\t':
264                         sp = 't';
265                         break;
266
267                 case '\\':
268                         sp = '\\';
269                         break;
270
271                 default:
272                         sp = '\0';
273                         break;
274                 } /* escape the character at *p */
275
276         do_escape:
277                 if (sp) {
278                         if ((freespace > 0) && (freespace <= 2)) {
279                                 if (out) out[used] = '\0';
280                                 out = NULL;
281                                 freespace = 0;
282
283                         } else if (freespace > 2) { /* room for char AND trailing zero */
284                                 if (out) {
285                                         out[used] = '\\';
286                                         out[used + 1] = sp;
287                                 }
288                                 freespace -= 2;
289                         }
290
291                         used += 2;
292                         p++;
293                         inlen--;
294                         continue;
295                 }
296
297                 /*
298                  *      All strings are UTF-8 clean.
299                  */
300                 utf8 = fr_utf8_char(p, inlen);
301
302                 /*
303                  *      If we have an invalid UTF-8 character, it gets
304                  *      copied over as a 1-byte character for single
305                  *      quoted strings.  Which means that the output
306                  *      isn't strictly UTF-8, but oh well...
307                  *
308                  *      For double quoted strints, the invalid
309                  *      characters get escaped as octal encodings.
310                  */
311                 if (utf8 == 0) {
312                         if (quote == '\'') {
313                                 utf8 = 1;
314
315                         } else {
316                                 if ((freespace > 0) && (freespace <= 4)) {
317                                         if (out) out[used] = '\0';
318                                         out = NULL;
319                                         freespace = 0;
320
321                                 } else if (freespace > 4) { /* room for char AND trailing zero */
322                                         if (out) snprintf(out + used, freespace, "\\%03o", *p);
323                                         freespace -= 4;
324                                 }
325
326                                 used += 4;
327                                 p++;
328                                 inlen--;
329                                 continue;
330                         }
331                 }
332
333                 if ((freespace > 0) && (freespace <= utf8)) {
334                         if (out) out[used] = '\0';
335                         out = NULL;
336                         freespace = 0;
337
338                 } else if (freespace > utf8) { /* room for char AND trailing zero */
339                         memcpy(out + used, p, utf8);
340                         freespace -= utf8;
341                 }
342
343                 used += utf8;
344                 p += utf8;
345                 inlen -= utf8;
346         }
347
348         /*
349          *      Ensure that the output buffer is always zero terminated.
350          */
351         if (out && freespace) out[used] = '\0';
352
353         return used;
354 }
355
356 /** Find the length of the buffer required to fully escape a string with fr_prints
357  *
358  * Were assuming here that's it's cheaper to figure out the length and do one
359  * alloc than repeatedly expand the buffer when we find extra chars which need
360  * to be added.
361  *
362  * @param in string to calculate the escaped length for.
363  * @param inlen length of the input string, if < 0 strlen will be used to check the length.
364  * @param[in] quote the quotation character.
365  * @return the size of buffer required to hold the escaped string including the NUL byte.
366  */
367 size_t fr_prints_len(char const *in, ssize_t inlen, char quote)
368 {
369         return fr_prints(NULL, 0, in, inlen, quote) + 1;
370 }
371
372 /** Escape string that may contain binary data, and write it to a new buffer
373  *
374  * This is useful in situations where we expect printable strings as input,
375  * but under some conditions may get binary data. A good example is libldap
376  * and the arrays of struct berval ldap_get_values_len returns.
377  *
378  * @param[in] ctx To allocate new buffer in.
379  * @param[in] in String to escape.
380  * @param[in] inlen Length of string. Should be >= 0 if the data may contain
381  *      embedded \0s. Must be >= 0 if data may not be \0 terminated.
382  *      If < 0 inlen will be calculated using strlen.
383  * @param[in] quote the quotation character.
384  * @return new buffer holding the escaped string.
385  */
386 char *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
387 {
388         size_t len, ret;
389         char *out;
390
391         len = fr_prints_len(in, inlen, quote);
392
393         out = talloc_array(ctx, char, len);
394         ret = fr_prints(out, len, in, inlen, quote);
395
396         /*
397          *      This is a fatal error, but fr_assert is the strongest
398          *      assert we're allowed to use in library functions.
399          */
400         if (!fr_assert(ret == (len - 1))) {
401                 talloc_free(out);
402                 return NULL;
403         }
404
405         return out;
406 }
407
408 /** Print the value of an attribute to a string
409  *
410  * @param[out] out Where to write the string.
411  * @param[in] outlen Size of outlen (must be at least 3 bytes).
412  * @param[in] vp to print.
413  * @param[in] quote Char to add before and after printed value, if 0 no char will be added, if < 0 raw string will be
414  *      added.
415  * @return the length of data written to out, or a value >= outlen on truncation.
416  */
417 size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
418 {
419         VERIFY_VP(vp);
420
421         if (vp->type == VT_XLAT) {
422                 return snprintf(out, outlen, "%c%s%c", quote, vp->value.xlat, quote);
423         }
424
425         return value_data_prints(out, outlen, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
426 }
427
428 /** Print one attribute value to a string
429  *
430  * @param ctx to allocate string in.
431  * @param vp to print.
432  * @param[in] quote the quotation character
433  * @return a talloced buffer with the attribute operator and value.
434  */
435 char *vp_aprints_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
436 {
437         VERIFY_VP(vp);
438
439         if (vp->type == VT_XLAT) {
440                 return fr_aprints(ctx, vp->value.xlat, talloc_array_length(vp->value.xlat) - 1, quote);
441         }
442
443         return value_data_aprints(ctx, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
444 }
445
446 char *vp_aprints_type(TALLOC_CTX *ctx, PW_TYPE type)
447 {
448         switch (type) {
449         case PW_TYPE_STRING :
450                 return talloc_typed_strdup(ctx, "_");
451
452         case PW_TYPE_INTEGER64:
453         case PW_TYPE_SIGNED:
454         case PW_TYPE_BYTE:
455         case PW_TYPE_SHORT:
456         case PW_TYPE_INTEGER:
457         case PW_TYPE_DATE :
458                 return talloc_typed_strdup(ctx, "0");
459
460         case PW_TYPE_IPV4_ADDR :
461                 return talloc_typed_strdup(ctx, "?.?.?.?");
462
463         case PW_TYPE_IPV4_PREFIX:
464                 return talloc_typed_strdup(ctx, "?.?.?.?/?");
465
466         case PW_TYPE_IPV6_ADDR:
467                 return talloc_typed_strdup(ctx, "[:?:]");
468
469         case PW_TYPE_IPV6_PREFIX:
470                 return talloc_typed_strdup(ctx, "[:?:]/?");
471
472         case PW_TYPE_OCTETS:
473                 return talloc_typed_strdup(ctx, "??");
474
475         case PW_TYPE_ETHERNET:
476                 return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
477
478 #ifdef WITH_ASCEND_BINARY
479         case PW_TYPE_ABINARY:
480                 return talloc_typed_strdup(ctx, "??");
481 #endif
482
483         default :
484                 break;
485         }
486
487         return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
488 }
489
490 /**  Prints attribute enumv escaped suitably for use as JSON enumv
491  *
492  *  Returns < 0 if the buffer may be (or have been) too small to write the encoded
493  *  JSON value to.
494  *
495  * @param out Where to write the string.
496  * @param outlen Length of output buffer.
497  * @param vp to print.
498  * @return the length of data written to out, or a value >= outlen on truncation.
499  */
500 size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
501 {
502         char const      *q;
503         size_t          len, freespace = outlen;
504
505         if (!vp->da->flags.has_tag) {
506                 switch (vp->da->type) {
507                 case PW_TYPE_INTEGER:
508                         if (vp->da->flags.has_value) break;
509
510                         return snprintf(out, freespace, "%u", vp->vp_integer);
511
512                 case PW_TYPE_SHORT:
513                         if (vp->da->flags.has_value) break;
514
515                         return snprintf(out, freespace, "%u", (unsigned int) vp->vp_short);
516
517                 case PW_TYPE_BYTE:
518                         if (vp->da->flags.has_value) break;
519
520                         return snprintf(out, freespace, "%u", (unsigned int) vp->vp_byte);
521
522                 case PW_TYPE_SIGNED:
523                         return snprintf(out, freespace, "%d", vp->vp_signed);
524
525                 default:
526                         break;
527                 }
528         }
529
530         /* Indicate truncation */
531         if (freespace < 2) return outlen + 1;
532         *out++ = '"';
533         freespace--;
534
535         switch (vp->da->type) {
536         case PW_TYPE_STRING:
537                 for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->vp_length; q++) {
538                         /* Indicate truncation */
539                         if (freespace < 3) return outlen + 1;
540
541                         if (*q == '"') {
542                                 *out++ = '\\';
543                                 *out++ = '"';
544                                 freespace -= 2;
545                         } else if (*q == '\\') {
546                                 *out++ = '\\';
547                                 *out++ = '\\';
548                                 freespace -= 2;
549                         } else if (*q == '/') {
550                                 *out++ = '\\';
551                                 *out++ = '/';
552                                 freespace -= 2;
553                         } else if (*q >= ' ') {
554                                 *out++ = *q;
555                                 freespace--;
556                         } else {
557                                 *out++ = '\\';
558                                 freespace--;
559
560                                 switch (*q) {
561                                 case '\b':
562                                         *out++ = 'b';
563                                         freespace--;
564                                         break;
565
566                                 case '\f':
567                                         *out++ = 'f';
568                                         freespace--;
569                                         break;
570
571                                 case '\n':
572                                         *out++ = 'b';
573                                         freespace--;
574                                         break;
575
576                                 case '\r':
577                                         *out++ = 'r';
578                                         freespace--;
579                                         break;
580
581                                 case '\t':
582                                         *out++ = 't';
583                                         freespace--;
584                                         break;
585                                 default:
586                                         len = snprintf(out, freespace, "u%04X", *q);
587                                         if (is_truncated(len, freespace)) return (outlen - freespace) + len;
588                                         out += len;
589                                         freespace -= len;
590                                 }
591                         }
592                 }
593                 break;
594
595         default:
596                 len = vp_prints_value(out, freespace, vp, 0);
597                 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
598                 out += len;
599                 freespace -= len;
600                 break;
601         }
602
603         /* Indicate truncation */
604         if (freespace < 2) return outlen + 1;
605         *out++ = '"';
606         freespace--;
607         *out = '\0'; // We don't increment out, because the nul byte should not be included in the length
608
609         return outlen - freespace;
610 }
611
612 /*
613  *  This is a hack, and has to be kept in sync with tokens.h
614  */
615 static char const *vp_tokens[] = {
616         "?",                    /* T_INVALID */
617         "EOL",                  /* T_EOL */
618         "{",
619         "}",
620         "(",
621         ")",
622         ",",
623         ";",
624         "++",
625         "+=",
626         "-=",
627         ":=",
628         "=",
629         "!=",
630         ">=",
631         ">",
632         "<=",
633         "<",
634         "=~",
635         "!~",
636         "=*",
637         "!*",
638         "==",
639         "#",
640         "<BARE-WORD>",
641         "<\"STRING\">",
642         "<'STRING'>",
643         "<`STRING`>"
644 };
645
646 /** Print one attribute and value to a string
647  *
648  * Print a VALUE_PAIR in the format:
649 @verbatim
650         <attribute_name>[:tag] <op> <value>
651 @endverbatim
652  * to a string.
653  *
654  * @param out Where to write the string.
655  * @param outlen Length of output buffer.
656  * @param vp to print.
657  * @return the length of data written to out, or a value >= outlen on truncation.
658  */
659 size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
660 {
661         char const      *token = NULL;
662         size_t          len, freespace = outlen;
663
664         if (!out) return 0;
665
666         *out = '\0';
667         if (!vp || !vp->da) return 0;
668
669         VERIFY_VP(vp);
670
671         if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
672                 token = vp_tokens[vp->op];
673         } else {
674                 token = "<INVALID-TOKEN>";
675         }
676
677         if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
678                 len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
679         } else {
680                 len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
681         }
682
683         if (is_truncated(len, freespace)) return len;
684         out += len;
685         freespace -= len;
686
687         len = vp_prints_value(out, freespace, vp, '"');
688         if (is_truncated(len, freespace)) return (outlen - freespace) + len;
689         freespace -= len;
690
691         return (outlen - freespace);
692 }
693
694 /** Print one attribute and value to FP
695  *
696  * Complete string with '\\t' and '\\n' is written to buffer before printing to
697  * avoid issues when running with multiple threads.
698  *
699  * @param fp to output to.
700  * @param vp to print.
701  */
702 void vp_print(FILE *fp, VALUE_PAIR const *vp)
703 {
704         char    buf[1024];
705         char    *p = buf;
706         size_t  len;
707
708         VERIFY_VP(vp);
709
710         *p++ = '\t';
711         len = vp_prints(p, sizeof(buf) - 1, vp);
712         if (!len) {
713                 return;
714         }
715         p += len;
716
717         /*
718          *      Deal with truncation gracefully
719          */
720         if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
721                 p = buf + (sizeof(buf) - 2);
722         }
723
724         *p++ = '\n';
725         *p = '\0';
726
727         fputs(buf, fp);
728 }
729
730
731 /** Print a list of attributes and enumv
732  *
733  * @param fp to output to.
734  * @param const_vp to print.
735  */
736 void vp_printlist(FILE *fp, VALUE_PAIR const *const_vp)
737 {
738         VALUE_PAIR *vp;
739         vp_cursor_t cursor;
740
741         memcpy(&vp, &const_vp, sizeof(vp)); /* const work-arounds */
742
743         for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) {
744                 vp_print(fp, vp);
745         }
746 }
747
748 /** Print one attribute and value to a string
749  *
750  * Print a VALUE_PAIR in the format:
751 @verbatim
752         <attribute_name>[:tag] <op> <value>
753 @endverbatim
754  * to a string.
755  *
756  * @param ctx to allocate string in.
757  * @param vp to print.
758  * @param[in] quote the quotation character
759  * @return a talloced buffer with the attribute operator and value.
760  */
761 char *vp_aprints(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
762 {
763         char const      *token = NULL;
764         char            *str, *value;
765
766         if (!vp || !vp->da) return 0;
767
768         VERIFY_VP(vp);
769
770         if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
771                 token = vp_tokens[vp->op];
772         } else {
773                 token = "<INVALID-TOKEN>";
774         }
775
776         value = vp_aprints_value(ctx, vp, quote);
777
778         if (vp->da->flags.has_tag) {
779                 if (quote && (vp->da->type == PW_TYPE_STRING)) {
780                         str = talloc_asprintf(ctx, "%s:%d %s %c%s%c", vp->da->name, vp->tag, token, quote, value, quote);
781                 } else {
782                         str = talloc_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value);
783                 }
784         } else {
785                 if (quote && (vp->da->type == PW_TYPE_STRING)) {
786                         str = talloc_asprintf(ctx, "%s %s %c%s%c", vp->da->name, token, quote, value, quote);
787                 } else {
788                         str = talloc_asprintf(ctx, "%s %s %s", vp->da->name, token, value);
789                 }
790         }
791
792         talloc_free(value);
793
794         return str;
795 }