2 * print.c Routines to print stuff.
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.
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.
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
20 * Copyright 2000,2006 The FreeRADIUS server project
25 #include <freeradius-devel/libradius.h>
30 * Checks for utf-8, taken from:
32 * http://www.w3.org/International/questions/qa-forms-utf-8
34 * Note that we don't care about the length of the input string,
35 * because '\0' is an invalid UTF-8 character.
37 int fr_utf8_char(uint8_t const *str)
39 if (*str < 0x20) return 0;
41 if (*str <= 0x7e) return 1; /* 1 */
43 if (*str <= 0xc1) return 0;
45 if ((str[0] >= 0xc2) && /* 2 */
52 if ((str[0] == 0xe0) && /* 3 */
60 if ((str[0] >= 0xe1) && /* 4a */
69 if ((str[0] >= 0xee) && /* 4b */
78 if ((str[0] == 0xed) && /* 5 */
86 if ((str[0] == 0xf0) && /* 6 */
96 if ((str[0] >= 0xf1) && /* 6 */
108 if ((str[0] == 0xf4) && /* 7 */
119 * Invalid UTF-8 Character
124 /** Escape any non printable or non-UTF8 characters in the input string
126 * @param[in] in string to escape.
127 * @param[in] inlen length of string to escape (lets us deal with embedded NULLs)
128 * @param[out] out where to write the escaped string.
129 * @param[out] outlen the length of the buffer pointed to by out.
130 * @return the number of bytes written to the out buffer, or a number > outlen if truncation has occurred.
132 size_t fr_print_string(char const *in, size_t inlen, char *out, size_t outlen)
134 uint8_t const *p = (uint8_t const *) in;
137 size_t freespace = outlen;
139 /* Can't '\0' terminate */
140 if (freespace == 0) {
144 /* No input, so no output... */
151 /* Figure out the length of the input string */
152 if (inlen == 0) inlen = strlen(in);
154 /* Not enough space to hold one char */
156 /* And there's input data... */
167 * Hack: never print trailing zero.
168 * Some clients send pings with an off-by-one
169 * length (confused with strings in C).
171 if ((inlen == 1) && (*p == '\0')) {
197 if (freespace < 3) break; /* \ + <c> + \0 */
206 utf8 = fr_utf8_char(p);
208 if (freespace < 5) break; /* \ + <o><o><o> + \0 */
209 snprintf(out, freespace, "\\%03o", *p);
218 if (freespace < 2) goto finish; /* <c> + \0 */
222 } while (--utf8 > 0);
228 /* Indicate truncation occurred */
229 if (inlen > 0) return outlen + inlen;
231 return outlen - freespace;
234 /** Find the length of the buffer required to fully escape a string with fr_print_string
236 * Were assuming here that's it's cheaper to figure out the length and do one
237 * alloc than repeatedly expand the buffer when we find extra chars which need
240 * @param in string to calculate the escaped length for.
241 * @param inlen length of the input string, if 0 strlen will be used to check the length.
242 * @return the size of buffer required to hold the escaped string excluding the NULL byte.
244 size_t fr_print_string_len(char const *in, size_t inlen)
246 uint8_t const *p = (uint8_t const *) in;
260 * Hack: never print trailing zero. Some clients send pings
261 * with an off-by-one length (confused with strings in C).
263 if ((inlen == 1) && (*p == '\0')) {
283 utf8 = fr_utf8_char(p);
295 } while (--utf8 > 0);
301 /** Print the value of an attribute to a string
304 size_t vp_data_prints_value(char *out, size_t outlen,
305 DICT_ATTR const *da, value_data_t const *data, size_t data_len, int8_t quote)
308 char buf[1024]; /* Interim buffer to use with poorly behaved printing functions */
309 char const *a = NULL;
313 size_t len = 0, freespace = outlen;
316 if (outlen == 0) return data_len;
322 /* need to copy the escaped value, but quoted */
328 *out++ = (char) quote;
331 len = fr_print_string(data->strvalue, data_len, out, freespace);
332 /* always terminate the quoted string with another quote */
333 if (len >= (freespace - 1)) {
334 out[outlen - 2] = (char) quote;
335 out[outlen - 1] = '\0';
341 *out++ = (char) quote;
348 /* xlat.c - need to copy raw value verbatim */
349 else if (quote < 0) {
350 if (outlen > data_len) {
351 memcpy(out, data->strvalue, data_len + 1);
355 memcpy(out, data->strvalue, outlen);
356 out[outlen - 1] = '\0';
357 return data_len; /* not a typo */
360 return fr_print_string(data->strvalue, data_len, out, outlen);
362 case PW_TYPE_INTEGER:
365 /* Normal, non-tagged attribute */
366 if ((v = dict_valbyattr(da->attr, da->vendor, data->integer)) != NULL) {
370 /* should never be truncated */
371 len = snprintf(buf, sizeof(buf), "%u", data->integer);
376 case PW_TYPE_INTEGER64:
377 return snprintf(out, outlen, "%" PRIu64, data->integer64);
382 len = strftime(buf, sizeof(buf) - 1, "%%%b %e %Y %H:%M:%S %Z%%", localtime_r(&t, &s_tm));
383 buf[0] = (char) quote;
384 buf[len - 1] = (char) quote;
387 len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z", localtime_r(&t, &s_tm));
392 case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
393 len = snprintf(buf, sizeof(buf), "%d", data->sinteger);
397 case PW_TYPE_IPV4_ADDR:
398 a = inet_ntop(AF_INET, &(data->ipaddr), buf, sizeof(buf));
402 case PW_TYPE_ABINARY:
403 #ifdef WITH_ASCEND_BINARY
404 print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, len, quote);
416 /* Return the number of bytes we would have written */
417 len = (data_len * 2) + 2;
418 if (freespace <= 1) {
425 if (freespace <= 1) {
432 if (freespace <= 2) {
437 /* Get maximum number of bytes we can encode given freespace */
438 max = ((freespace % 2) ? freespace - 1 : freespace - 2) / 2;
439 fr_bin2hex(out, data->octets, (data_len > max) ? max : data_len);
444 a = ifid_ntoa(buf, sizeof(buf), data->ifid);
448 case PW_TYPE_IPV6_ADDR:
449 a = inet_ntop(AF_INET6, &data->ipv6addr, buf, sizeof(buf));
453 case PW_TYPE_IPV6_PREFIX:
455 struct in6_addr addr;
460 memcpy(&addr, &(data->ipv6prefix[2]), sizeof(addr));
462 a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
468 len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) data->ipv6prefix[1]);
473 case PW_TYPE_IPV4_PREFIX:
480 memcpy(&addr, &(data->ipv4prefix[2]), sizeof(addr));
482 a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
488 len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) (data->ipv4prefix[1] & 0x3f));
493 case PW_TYPE_ETHERNET:
494 return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
495 data->ether[0], data->ether[1],
496 data->ether[2], data->ether[3],
497 data->ether[4], data->ether[5]);
505 if (a) strlcpy(out, a, outlen);
507 return len; /* Return the number of bytes we would of written (for truncation detection) */
510 /** Print the value of an attribute to a string
512 * @param[out] out Where to write the string.
513 * @param[in] outlen Size of outlen (must be at least 3 bytes).
514 * @param[in] vp to print.
515 * @param[in] quote Char to add before and after printed value, if 0 no char will be added, if < 0 raw string will be
517 * @return the length of data written to out, or a value >= outlen on truncation.
519 size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t quote)
521 return vp_data_prints_value(out, outlen, vp->da, &vp->data, vp->length, quote);
524 char *vp_aprint_type(TALLOC_CTX *ctx, PW_TYPE type)
527 case PW_TYPE_STRING :
528 return talloc_typed_strdup(ctx, "_");
530 case PW_TYPE_INTEGER64:
534 case PW_TYPE_INTEGER:
536 return talloc_typed_strdup(ctx, "0");
538 case PW_TYPE_IPV4_ADDR :
539 return talloc_typed_strdup(ctx, "?.?.?.?");
541 case PW_TYPE_IPV4_PREFIX:
542 return talloc_typed_strdup(ctx, "?.?.?.?/?");
544 case PW_TYPE_IPV6_ADDR:
545 return talloc_typed_strdup(ctx, "[:?:]");
547 case PW_TYPE_IPV6_PREFIX:
548 return talloc_typed_strdup(ctx, "[:?:]/?");
551 return talloc_typed_strdup(ctx, "??");
553 case PW_TYPE_ETHERNET:
554 return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
556 #ifdef WITH_ASCEND_BINARY
557 case PW_TYPE_ABINARY:
558 return talloc_typed_strdup(ctx, "??");
565 return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
568 /** Prints attribute values escaped suitably for use as JSON values
570 * Returns < 0 if the buffer may be (or have been) too small to write the encoded
573 * @param out Where to write the string.
574 * @param outlen Lenth of output buffer.
575 * @param vp to print.
576 * @return the length of data written to out, or a value >= outlen on truncation.
578 size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
581 size_t len, freespace = outlen;
583 if (!vp->da->flags.has_tag) {
584 switch (vp->da->type) {
585 case PW_TYPE_INTEGER:
588 if (vp->da->flags.has_value) break;
590 return snprintf(out, freespace, "%u", vp->vp_integer);
593 return snprintf(out, freespace, "%d", vp->vp_signed);
600 /* Indicate truncation */
601 if (freespace < 2) return outlen + 1;
605 switch (vp->da->type) {
607 for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->length; q++) {
608 /* Indicate truncation */
609 if (freespace < 3) return outlen + 1;
615 } else if (*q == '\\') {
619 } else if (*q == '/') {
623 } else if (*q >= ' ') {
656 len = snprintf(out, freespace, "u%04X", *q);
657 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
666 len = vp_prints_value(out, freespace, vp, 0);
667 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
673 /* Indicate truncation */
674 if (freespace < 2) return outlen + 1;
677 *out = '\0'; // We don't increment out, because the nul byte should not be included in the length
679 return outlen - freespace;
683 * This is a hack, and has to be kept in sync with tokens.h
685 static char const *vp_tokens[] = {
686 "?", /* T_OP_INVALID */
716 extern int fr_attr_max_tlv;
717 extern int fr_attr_shift[];
718 extern int fr_attr_mask[];
721 /** Print one attribute and value to a string
723 * Print a VALUE_PAIR in the format:
725 <attribute_name>[:tag] <op> <value>
729 * @param out Where to write the string.
730 * @param outlen Lenth of output buffer.
731 * @param vp to print.
732 * @return the length of data written to out, or a value >= outlen on truncation.
734 size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
736 char const *token = NULL;
737 size_t len, freespace = outlen;
742 if (!vp || !vp->da) return 0;
746 if ((vp->op > T_OP_INVALID) && (vp->op < T_TOKEN_LAST)) {
747 token = vp_tokens[vp->op];
749 token = "<INVALID-TOKEN>";
752 if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
753 len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
755 len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
758 if (is_truncated(len, freespace)) return len;
762 len = vp_prints_value(out, freespace, vp, '\'');
763 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
766 return (outlen - freespace);
769 /** Print one attribute and value to FP
771 * Complete string with '\\t' and '\\n' is written to buffer before printing to
772 * avoid issues when running with multiple threads.
774 * @param fp to output to.
775 * @param vp to print.
777 void vp_print(FILE *fp, VALUE_PAIR const *vp)
786 len = vp_prints(p, sizeof(buf) - 1, vp);
793 * Deal with truncation gracefully
795 if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
796 p = buf + (sizeof(buf) - 2);
806 /** Print a list of attributes and values
808 * @param fp to output to.
809 * @param vp to print.
811 void vp_printlist(FILE *fp, VALUE_PAIR const *vp)
814 for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) {
821 * vp_prints_value for talloc
823 char *vp_aprint_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
827 switch (vp->da->type) {
832 /* Gets us the size of the buffer we need to alloc */
833 len = fr_print_string_len(vp->vp_strvalue, vp->length);
834 p = talloc_array(ctx, char, len + 1); /* +1 for '\0' */
837 ret = fr_print_string(vp->vp_strvalue, vp->length, p, len + 1);
838 if (!fr_assert(ret == len)) {
847 case PW_TYPE_INTEGER:
851 dv = dict_valbyattr(vp->da->attr, vp->da->vendor,
854 p = talloc_typed_strdup(ctx, dv->name);
856 p = talloc_typed_asprintf(ctx, "%u", vp->vp_integer);
862 p = talloc_typed_asprintf(ctx, "%d", vp->vp_signed);
865 case PW_TYPE_INTEGER64:
866 p = talloc_typed_asprintf(ctx, "%" PRIu64 , vp->vp_integer64);
869 case PW_TYPE_ETHERNET:
870 p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
871 vp->vp_ether[0], vp->vp_ether[1],
872 vp->vp_ether[2], vp->vp_ether[3],
873 vp->vp_ether[4], vp->vp_ether[5]);
876 case PW_TYPE_ABINARY:
877 #ifdef WITH_ASCEND_BINARY
878 p = talloc_array(ctx, char, 128);
880 print_abinary(p, 128, (uint8_t *) &vp->vp_filter, vp->length, 0);
887 p = talloc_array(ctx, char, 1 + vp->length * 2);
889 fr_bin2hex(p, vp->vp_octets, vp->length);
899 p = talloc_array(ctx, char, 64);
900 strftime(p, 64, "%b %e %Y %H:%M:%S %Z",
901 localtime_r(&t, &s_tm));
906 * We need to use the proper inet_ntop functions for IP
907 * addresses, else the output might not match output of
908 * other functions, which makes testing difficult.
910 * An example is tunnelled ipv4 in ipv6 addresses.
912 case PW_TYPE_IPV4_ADDR:
913 case PW_TYPE_IPV4_PREFIX:
915 char buff[INET_ADDRSTRLEN + 4]; // + /prefix
918 vp_prints_value(buff, sizeof(buff), vp, 0);
920 p = talloc_typed_strdup(ctx, buff);
924 case PW_TYPE_IPV6_ADDR:
925 case PW_TYPE_IPV6_PREFIX:
927 char buff[INET6_ADDRSTRLEN + 4]; // + /prefix
930 vp_prints_value(buff, sizeof(buff), vp, 0);
932 p = talloc_typed_strdup(ctx, buff);
937 p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
938 (vp->vp_ifid[0] << 8) | vp->vp_ifid[1],
939 (vp->vp_ifid[2] << 8) | vp->vp_ifid[3],
940 (vp->vp_ifid[4] << 8) | vp->vp_ifid[5],
941 (vp->vp_ifid[6] << 8) | vp->vp_ifid[7]);
952 /** Print one attribute and value to a string
954 * Print a VALUE_PAIR in the format:
956 <attribute_name>[:tag] <op> <value>
960 * @param ctx to allocate string in.
961 * @param vp to print.
962 * @return a talloced buffer with the attribute operator and value.
964 char *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
966 char const *token = NULL;
969 if (!vp || !vp->da) return 0;
973 if ((vp->op > T_OP_INVALID) && (vp->op < T_TOKEN_LAST)) {
974 token = vp_tokens[vp->op];
976 token = "<INVALID-TOKEN>";
979 value = vp_aprint_value(ctx, vp);
980 pair = vp->da->flags.has_tag ?
981 talloc_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value) :
982 talloc_asprintf(ctx, "%s %s %s", vp->da->name, token, value);